osra-2.1.3/0000775000175000017500000000000014115175270011150 5ustar igorigorosra-2.1.3/pom.xml.in0000664000175000017500000000407314115175252013076 0ustar igorigor 4.0.0 net.sf.osra osra jar @PACKAGE_VERSION@ OSRA http://osra.sourceforge.net/ https://osra.svn.sourceforge.net/svnroot/osra/trunk SourceForge https://sourceforge.net/tracker/?group_id=203833&atid=987182 GPLv2 License http://www.gnu.org/licenses/gpl-2.0.txt net.sf.jnati jnati-deploy 0.4 log4j log4j junit junit 4.8.2 test commons-io commons-io 1.4 test maven-deploy-plugin 2.7 addons/java osra-2.1.3/package/0000775000175000017500000000000014115175252012543 5ustar igorigorosra-2.1.3/package/win32/0000775000175000017500000000000014115175252013505 5ustar igorigorosra-2.1.3/package/win32/osra.nsi.in0000775000175000017500000002334514115175252015603 0ustar igorigor!define DOT_VERSION "@PACKAGE_VERSION@" !define DASH_VERSION "@PACKAGE_VERSION_WITH_DASHES@" !include Sections.nsh ; include for some of the windows messages defines !include "winmessages.nsh" ; HKLM (all users) vs HKCU (current user) defines !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' !define env_hkcu 'HKCU "Environment"' !include "TextFunc.nsh" !insertmacro LineFind ; The name of the installer Name "Optical Structure Recognition Application" ; The file to write OutFile "osra-setup-${DASH_VERSION}.exe" ; The default installation directory InstallDir $PROGRAMFILES\osra\${DOT_VERSION} ; Registry key to check for directory (so if you install again, it will ; overwrite the old one automatically) InstallDirRegKey HKLM "Software\osra\${DOT_VERSION}" "Install_Dir" LicenseData "license.txt" ; Request application privileges for Windows Vista RequestExecutionLevel admin ;-------------------------------- ; Pages Page license Page components Page directory Page instfiles UninstPage uninstConfirm UninstPage instfiles ;-------------------------------- ; The stuff to install Section "osra (required)" SectionIn RO ; Set output path to the installation directory. SetOutPath $INSTDIR ; Put file there File "osra-bin.exe" ; File "pthreadGC2.dll" File "README.txt" File "spelling.txt" File "superatom.txt" call createOSRAbat ; Write the installation path into the registry WriteRegStr HKLM SOFTWARE\osra\${DOT_VERSION} "Install_Dir" "$INSTDIR" WriteRegStr HKLM SOFTWARE\osra "Install_Dir" "$INSTDIR" ; Write the uninstall keys for Windows WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\osra" "DisplayName" "OSRA ${DOT_VERSION}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\osra" "UninstallString" '"$INSTDIR\uninstall.exe"' WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\osra" "NoModify" 1 WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\osra" "NoRepair" 1 WriteUninstaller "uninstall.exe" ; set variable WriteRegExpandStr ${env_hklm} OSRA "$INSTDIR" ; make sure windows knows about the change SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 SectionEnd Section /o "BIOVIA Draw plugin" symyx_draw call getSymyxPath strcmp $1 "" no_symyx SetOutPath "$1\AddIns" File "plugins\symyx_draw\OSRAAction.xml" SetOutPath "$1\AddIns\OSRAAction" File "plugins\symyx_draw\OSRAAction\README.txt" File "plugins\symyx_draw\OSRAAction\OSRAAction.dll" Goto done no_symyx: MessageBox MB_OK "BIOVIA Draw not found" IDOK done done: SectionEnd ; Uninstaller Section "Uninstall" # call userInfo plugin to get user info. The plugin puts the result in the stack userInfo::getAccountType # pop the result from the stack into $0 pop $0 # compare the result with the string "Admin" to see if the user is admin. # If match, jump 3 lines down. strCmp $0 "Admin" +3 # if there is not a match, print message and return messageBox MB_OK "Please run this with Administrator privileges" Quit ReadRegStr $0 HKLM SOFTWARE\osra\${DOT_VERSION} "Install_Dir" strcpy $INSTDIR $0 ; Remove registry keys DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\osra" DeleteRegKey HKLM SOFTWARE\osra\${DOT_VERSION} ; delete variable DeleteRegValue ${env_hklm} OSRA ; make sure windows knows about the change SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 ; Remove files and uninstaller Delete $INSTDIR\osra-bin.exe ; Delete $INSTDIR\pthreadGC2.dll Delete $INSTDIR\README.txt Delete $INSTDIR\osra.bat Delete $INSTDIR\superatom.txt Delete $INSTDIR\spelling.txt Delete $INSTDIR\uninstall.exe RMDir "$INSTDIR" call un.getSymyxPath strcmp $1 "" no_symyx Delete "$1\AddIns\OSRAAction.xml" Delete "$1\AddIns\OSRAAction\README.txt" Delete "$1\AddIns\OSRAAction\OSRAAction.dll" Delete "$1\AddIns\OSRAAction\OSRAAction.dll.config" RMDir "$1\AddIns\OSRAAction" no_symyx: SectionEnd Function getSymyxPath Push "$PROGRAMFILES\BIOVIA" Push "BIOVIADraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1BIOVIADraw.exe fin Push "$PROGRAMFILES64\BIOVIA" Push "BIOVIADraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1BIOVIADraw.exe fin Push "$PROGRAMFILES\Accelrys" Push "AccelrysDraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1AccelrysDraw.exe fin Push "$PROGRAMFILES64\Accelrys" Push "AccelrysDraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1AccelrysDraw.exe fin Push "$PROGRAMFILES\Symyx" Push "SymyxDraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1SymyxDraw.exe fin Push "$PROGRAMFILES64\Symyx" Push "SymyxDraw.exe" Call FindIt Pop $R1 Push "$R1" Call GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1SymyxDraw.exe fin StrCpy $1 "" fin: ;$1 contains the folder of Symyx Draw or empty FunctionEnd Function un.getSymyxPath Push "$PROGRAMFILES\BIOVIA" Push "BIOVIADraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1BIOVIADraw.exe fin Push "$PROGRAMFILES64\BIOVIA" Push "BIOVIADraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1BIOVIADraw.exe fin Push "$PROGRAMFILES\Accelrys" Push "AccelrysDraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1AccelrysDraw.exe fin Push "$PROGRAMFILES64\Accelrys" Push "AccelrysDraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1AccelrysDraw.exe fin Push "$PROGRAMFILES\Symyx" Push "SymyxDraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1SymyxDraw.exe fin Push "$PROGRAMFILES64\Symyx" Push "SymyxDraw.exe" Call un.FindIt Pop $R1 Push "$R1" Call un.GetParent Pop $R0 StrCpy $1 "$R0\" IfFileExists $1SymyxDraw.exe fin StrCpy $1 "" fin: ;$1 contains the folder of Symyx Draw or empty FunctionEnd Function createOSRAbat fileOpen $0 "$INSTDIR\osra.bat" w fileWrite $0 '\ @echo off$\r$\n\ setlocal$\r$\n\ set exec_dir=%~dp0%$\r$\n\ set PATH=%exec_dir%;$1\bin;$1\lib;%PATH%$\r$\n\ "%exec_dir%osra-bin.exe" %*$\r$\n\ endlocal$\r$\n\ ' fileClose $0 FunctionEnd Function .onInit # call userInfo plugin to get user info. The plugin puts the result in the stack userInfo::getAccountType # pop the result from the stack into $0 pop $0 # compare the result with the string "Admin" to see if the user is admin. # If match, jump 3 lines down. strCmp $0 "Admin" +3 # if there is not a match, print message and return messageBox MB_OK "Please run this with Administrator privileges" Quit call getSymyxPath strcmp $1 "" no_symyx SectionGetFlags "${symyx_draw}" $0 IntOp $0 $0 | ${SF_SELECTED} SectionSetFlags "${symyx_draw}" $0 no_symyx: FunctionEnd Function FindIt Exch $R0 Exch Exch $R1 Push $R2 Push $R3 Push $R4 Push $R5 Push $R6 StrCpy $R6 -1 StrCpy $R3 1 Push $R1 nextDir: Pop $R1 IntOp $R3 $R3 - 1 ClearErrors FindFirst $R5 $R2 "$R1\*.*" nextFile: StrCmp $R2 "." gotoNextFile StrCmp $R2 ".." gotoNextFile StrCmp $R2 $R0 0 isDir StrCpy $R6 "$R1\$R2" loop: StrCmp $R3 0 done Pop $R1 IntOp $R3 $R3 - 1 Goto loop isDir: IfFileExists "$R1\$R2\*.*" 0 gotoNextFile IntOp $R3 $R3 + 1 Push "$R1\$R2" gotoNextFile: FindNext $R5 $R2 IfErrors 0 nextFile done: FindClose $R5 StrCmp $R3 0 0 nextDir StrCpy $R0 $R6 Pop $R6 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd Function GetParent Exch $R0 Push $R1 Push $R2 Push $R3 StrCpy $R1 0 StrLen $R2 $R0 loop: IntOp $R1 $R1 + 1 IntCmp $R1 $R2 get 0 get StrCpy $R3 $R0 1 -$R1 StrCmp $R3 "\" get Goto loop get: StrCpy $R0 $R0 -$R1 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd Function un.FindIt Exch $R0 Exch Exch $R1 Push $R2 Push $R3 Push $R4 Push $R5 Push $R6 StrCpy $R6 -1 StrCpy $R3 1 Push $R1 nextDir: Pop $R1 IntOp $R3 $R3 - 1 ClearErrors FindFirst $R5 $R2 "$R1\*.*" nextFile: StrCmp $R2 "." gotoNextFile StrCmp $R2 ".." gotoNextFile StrCmp $R2 $R0 0 isDir StrCpy $R6 "$R1\$R2" loop: StrCmp $R3 0 done Pop $R1 IntOp $R3 $R3 - 1 Goto loop isDir: IfFileExists "$R1\$R2\*.*" 0 gotoNextFile IntOp $R3 $R3 + 1 Push "$R1\$R2" gotoNextFile: FindNext $R5 $R2 IfErrors 0 nextFile done: FindClose $R5 StrCmp $R3 0 0 nextDir StrCpy $R0 $R6 Pop $R6 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEnd Function un.GetParent Exch $R0 Push $R1 Push $R2 Push $R3 StrCpy $R1 0 StrLen $R2 $R0 loop: IntOp $R1 $R1 + 1 IntCmp $R1 $R2 get 0 get StrCpy $R3 $R0 1 -$R1 StrCmp $R3 "\" get Goto loop get: StrCpy $R0 $R0 -$R1 Pop $R3 Pop $R2 Pop $R1 Exch $R0 FunctionEndosra-2.1.3/package/android/0000775000175000017500000000000014115175252014163 5ustar igorigorosra-2.1.3/package/android/runosra.java0000664000175000017500000000600414115175252016517 0ustar igorigorpackage cadd.osra.main; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URLEncoder; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.widget.TextView; public class runosra extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); try { writeToStream(getAssets().open("spelling.txt"), openFileOutput("spelling.txt", MODE_WORLD_READABLE )); } catch (Exception e) { e.printStackTrace(); } try { writeToStream(getAssets().open("superatom.txt"), openFileOutput("superatom.txt", MODE_WORLD_READABLE)); } catch (Exception e) { e.printStackTrace(); } byte [] rawimage=null; try { rawimage=writeToArray(getAssets().open("c.jpg")); } catch (Exception e) { e.printStackTrace(); } File spelling=getFileStreamPath("spelling.txt"); File superatom=getFileStreamPath("superatom.txt"); //File image=getFileStreamPath("chemnav.png"); //TextView tv = new TextView(this); String [] jargv= {"osra","-f","inchi","-l",spelling.getAbsolutePath(),"-a",superatom.getAbsolutePath()}; String inchi=nativeosra(jargv,rawimage); String enc_inchi=""; try { enc_inchi=URLEncoder.encode(inchi, "UTF-8"); } catch (Exception e) { e.printStackTrace(); } if (enc_inchi.length()!=0) { //String base_url="http://en.wikipedia.org/wiki/Special:Search?fulltext=Search&search="; String base_url="http://129.43.27.140/cgi-bin/lookup/results?type=inchi&context_all=all&query="; String url=base_url+enc_inchi; Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } //tv.setText( enc_inchi ); //setContentView(tv); } public native String nativeosra(String [] jargv, byte [] rawimage); static { System.loadLibrary("openbabel"); System.loadLibrary("osra"); } public static void writeToStream(InputStream in , OutputStream out) throws IOException { byte[] bytes = new byte[2048]; for (int c = in.read(bytes); c != -1; c = in.read(bytes)) { out.write(bytes,0, c); } in.close(); out.close(); } public static byte [] writeToArray(InputStream in) throws IOException { ByteArrayOutputStream out=new ByteArrayOutputStream(); byte[] bytes = new byte[2048]; for (int c = in.read(bytes); c != -1; c = in.read(bytes)) { out.write(bytes,0, c); } in.close(); //byte [] prep = new byte[out.size()]; byte [] arr=out.toByteArray(); out.close(); return arr; } } osra-2.1.3/package/osx/0000775000175000017500000000000014115175252013354 5ustar igorigorosra-2.1.3/package/osx/install.sh.in0000775000175000017500000000120214115175252015761 0ustar igorigor#!/bin/bash if (( $EUID != 0 )); then echo "The installation must be run as root" 1>&2 exit 1 fi mkdir -p /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@ || { echo "Cannot create /opt/local/@PACKAGE_NAME@ folder" 1>&2; exit; } mkdir -p /usr/local/bin || { echo "Cannot create /usr/local/bin folder" 1>&2; exit; } cp -f package/* /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@/ || { echo "Cannot copy to /opt/local/@PACKAGE_NAME@ folder" 1>&2; exit; } echo "Installing binary files in /opt/local/@PACKAGE_NAME@" cp -f osra /usr/local/bin || { echo "Cannot copy to /usr/local/bin" 1>&2; exit; } echo "Installing osra script in /usr/local/bin" osra-2.1.3/package/osx/INSTALL0000664000175000017500000000041214115175252014402 0ustar igorigorTo install run (as a root or via sudo) ./install.sh It will copy the contents of "package" into /opt/local/osra and the wrap-around shell script "osra" into /usr/local/bin Starting with version 2.1.0 Ghostscript is no longer necessary to process PDF and PS files. osra-2.1.3/package/osx/setup_env0000775000175000017500000000057014115175252015314 0ustar igorigor#!/bin/sh defaults write ~/.MacOSX/environment OSRA /usr/local/bin/ chown $USER ~/.MacOSX/environment.plist chmod a+r ~/.MacOSX/environment.plist launchctl setenv OSRA /usr/local/bin/ chmod a+rx /usr/local/bin/osra chmod a+rx /usr/local/lib/libopenabel.3.dylib chmod a+rx /usr/local/lib/libopenabel.dylib chmod a+rx /usr/local/lib/libopenabel.la chmod -R a+rx /opt/local/osra/osra-2.1.3/package/osx/osra.sh.in0000775000175000017500000000010214115175252015255 0ustar igorigor#!/bin/sh /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@/osra-bin $* osra-2.1.3/package/linux/0000775000175000017500000000000014115175252013702 5ustar igorigorosra-2.1.3/package/linux/install-user.sh.in0000775000175000017500000000051014115175251017263 0ustar igorigor#!/bin/bash mkdir -p $HOME/@PACKAGE_NAME@/@PACKAGE_VERSION@ || { echo "Cannot create $HOME/@PACKAGE_NAME@ folder" 1>&2; exit; } cp --remove-destination package/* $HOME/@PACKAGE_NAME@/@PACKAGE_VERSION@/ || { echo "Cannot copy to $HOME/@PACKAGE_NAME@ folder" 1>&2; exit; } echo "Installing binary files in $HOME/@PACKAGE_NAME@" osra-2.1.3/package/linux/suse/0000775000175000017500000000000014115175251014660 5ustar igorigorosra-2.1.3/package/linux/suse/osra.spec.in0000664000175000017500000001015614115175251017110 0ustar igorigor# # spec file for package OSRA # # Copyright (c) 2009 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed # upon. The license for this file, and modifications and additions to the # file, is the same license as for the pristine package itself (unless the # license for the pristine package is not an Open Source License, in which # case the license is the MIT License). An "Open Source License" is a # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. # Please submit bugfixes or comments via http://bugs.opensuse.org/ # # norootforbuild %define name @PACKAGE_NAME@ %define version @PACKAGE_VERSION@ %define builddep glibc-devel, libstdc++45-devel, tclap >= 1.2, potrace-devel >= 1.8, gocr-devel >= 0.49, ocrad-devel >= 0.20, libopenbabel-devel >= 2.3, libGraphicsMagick++-devel >= 1.3.10, cuneiform-devel => 1.1.0, tesseract-devel => 3.01, docbook-xsl-stylesheets => 1.74.0, libxslt %define binarydep potrace-lib >= 1.8, libopenbabel3 >= 2.3, libGraphicsMagick++3 >= 1.3.10, cuneiform => 1.1.0, tesseract => 3.01, %{name}-common = %{version} Name: %{name} BuildRequires: %{builddep} Url: http://osra.sourceforge.net/ Summary: A command line chemical structure recognition tool Version: %{version} Release: 1.0 Group: Productivity/Graphics/Other Requires: %{binarydep} License: GPL v2 or later Source0: %{name}-%{version}.tar.bz2 #Patch0: Makefile.in.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: @build_cpu@ %description OSRA is a utility designed to convert graphical representations of chemical structures into SMILES or SDF. OSRA can read a document in any of the over 90 graphical formats parseable by GraphicMagick and generate the SMILES or SDF representation of the molecular structure images encountered within that document. Authors: -------- Igor Filippov %package common Summary: OSRA shared files Group: Productivity/Graphics/Other BuildArch: noarch %description common This package contains the shared files for OSRA executable / library. %package lib@LIB_MAJOR_VERSION@ Summary: OSRA C++ library Group: Development/Libraries/C and C++ Requires: %{binarydep} %description lib@LIB_MAJOR_VERSION@ This package contains the dynamic library needed to consume OSRA functionality from C++ programs. %package lib-java@LIB_MAJOR_VERSION@ Summary: OSRA Java library Group: Development/Libraries/C and C++ Requires: %{binarydep} %description lib-java@LIB_MAJOR_VERSION@ This package contains the dynamic library needed to consume OSRA functionality from Java programs. %package devel Summary: OSRA static library and header files mandatory for development Group: Development/Libraries/C and C++ Requires: %{name}-lib@LIB_MAJOR_VERSION@ = %{version} %description devel This package contains all necessary include files and libraries needed to develop applications on the top of OSRA. %prep %setup -n %{name}-%{version} #%patch0 -p0 %build # See http://stackoverflow.com/questions/3113472/how-to-make-an-rpm-spec-that-installs-libraries-to-usr-lib-xor-usr-lib64-based # See http://www.rpm.org/api/4.4.2.2/config_macros.html %configure --enable-docs --enable-lib --enable-java --with-tesseract --with-cuneiform --datadir=%{_datadir}/%{name} --docdir=%{_datadir}/doc/packages/%{name} %__make %install # See http://fedoraproject.org/wiki/PackagingGuidelines#Why_the_.25makeinstall_macro_should_not_be_used %__make install DESTDIR=%{buildroot} %clean %__rm -rf $RPM_BUILD_ROOT %define _sharedir %{_prefix}/share %files %defattr(-, root, root) %{_prefix}/bin/%{name} %{_mandir}/man?/%{name}.* %files common %{_sharedir}/%{name} %{_sharedir}/doc %files lib@LIB_MAJOR_VERSION@ %defattr(-,root,root) %{_libdir}/lib%{name}.so* %files lib-java@LIB_MAJOR_VERSION@ %defattr(-,root,root) %{_libdir}/lib%{name}_java.so* %files devel %defattr(-,root,root) %{_libdir}/lib%{name}.a %{_libdir}/pkgconfig %{_includedir} # spec file ends here %changelog * Thu Jul 01 2011 dma_k@mail.ru - Initial SuSE package osra-2.1.3/package/linux/debian/0000775000175000017500000000000014115175251015123 5ustar igorigorosra-2.1.3/package/linux/debian/compat0000664000175000017500000000000214115175251016321 0ustar igorigor7 osra-2.1.3/package/linux/debian/changelog0000664000175000017500000000022114115175251016770 0ustar igorigorosra (2.0.0-1) unstable; urgency=low * Initial release (closes: #682760) -- Dmitry Katsubo Mon, 15 Dec 2014 17:05:38 +0200 osra-2.1.3/package/linux/debian/patches/0000775000175000017500000000000014115175251016552 5ustar igorigorosra-2.1.3/package/linux/debian/patches/series0000664000175000017500000000000014115175251017755 0ustar igorigorosra-2.1.3/package/linux/debian/copyright.new0000664000175000017500000000043614115175251017651 0ustar igorigorFormat-Specification: http://svn.debian.org/wsvn/dep/web/deps/dep5.mdwn?op=file&rev=135 Name: osra Maintainer: Dmitry Katsubo Source: https://osra.svn.sourceforge.net/svnroot/osra/ Files: * Copyright: 2007-2013 Igor Filippov License: GPL-2+ osra-2.1.3/package/linux/debian/libosra.install0000664000175000017500000000002614115175251020144 0ustar igorigor/usr/lib/libosra.so* osra-2.1.3/package/linux/debian/libosra-dev.install0000664000175000017500000000006614115175251020724 0ustar igorigor/usr/lib/libosra.a /usr/lib/pkgconfig /usr/include osra-2.1.3/package/linux/debian/watch0000664000175000017500000000040214115175251016150 0ustar igorigor# You can run the "uscan" command to check for upstream updates and more. # See uscan(1) for format # Compulsory line, this is a version 3 file version=3 # Uncomment to find new files on sourceforge, for devscripts >= 2.9 http://sf.net/osra/osra-(.*)\.tgz osra-2.1.3/package/linux/debian/osra-common.install0000664000175000017500000000004114115175251020740 0ustar igorigor/usr/share/osra /usr/share/doc osra-2.1.3/package/linux/debian/copyright0000664000175000017500000000216414115175251017061 0ustar igorigorThis work was packaged for Debian by: Dmitry Katsubo on Tue, 06 Jul 2010 12:48:38 -0500 It was downloaded from: https://sourceforge.net/projects/osra/files/osra/ Upstream Author: Igor Filippov Copyright: Copyright (C) 2007-2013 Igor Filippov License: This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". osra-2.1.3/package/linux/debian/control.in0000664000175000017500000000356514115175251017144 0ustar igorigorSource: @PACKAGE_NAME@ Section: science Priority: optional Maintainer: Dmitry Katsubo Build-Depends: debhelper (>= 9), cimg-dev (>= 1.2.7), libc6-dev (>= 2.7), libstdc++-dev, libtclap-dev (>= 1.2), libpotrace-dev (>= 1.8), libgocr-dev (>= 0.49), libocrad-dev (>= 0.20), libopenbabel-dev (>= 2.3), libgraphicsmagick++1-dev (>= 1.3), libcuneiform-dev (>= 1.1), libtesseract-dev (>= 3.01), java2-sdk, docbook-xsl (>= 1.74.0), docbook-xml, xsltproc, naturaldocs Standards-Version: 3.9.6 Homepage: http://osra.sourceforge.net/ XS-Vcs-Svn: https://osra.svn.sourceforge.net/svnroot/osra/ Vcs-browser: http://osra.svn.sourceforge.net/viewvc/osra/ Package: @PACKAGE_NAME@ Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, ${binary:Depends}, @PACKAGE_NAME@-common (= ${source:Version}) Recommends: gocr, ocrad, potrace Description: Command line chemical structure recognition tool (OSRA) ${common:Description} Package: @PACKAGE_NAME@-common Architecture: all Depends: ${misc:Depends} Description: Shared files for chemical structure recognition tool (OSRA) ${common:Description} Package: lib@PACKAGE_NAME@@LIB_MAJOR_VERSION@ Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, ${binary:Depends}, @PACKAGE_NAME@-common (= ${source:Version}) Description: Chemical structure recognition library (OSRA) ${common:Description} Package: lib@PACKAGE_NAME@-dev Architecture: any Section: libdevel Depends: ${misc:Depends}, lib@PACKAGE_NAME@@LIB_MAJOR_VERSION@ (= ${binary:Version}) Description: Development headers to consume the OSRA library ${common:Description} Package: lib@PACKAGE_NAME@-java@LIB_MAJOR_VERSION@ Architecture: any Section: java Depends: ${misc:Depends}, ${shlibs:Depends}, ${binary:Depends}, @PACKAGE_NAME@-common (= ${source:Version}) Description: Chemical structure recognition library for Java (OSRA) ${common:Description} osra-2.1.3/package/linux/debian/osra.install0000664000175000017500000000003214115175251017452 0ustar igorigor/usr/bin /usr/share/man osra-2.1.3/package/linux/debian/substvars0000664000175000017500000000123714115175251017105 0ustar igorigor# Note: underscore (_) is not allowed in variable name binary:Depends=libpotrace0 (>= 1.8), libopenbabel4 (>= 2.3), libgraphicsmagick++3 (>= 1.3), libtesseract3 (>= 3.01), libcuneiform0 (>= 1.1) common:Description=OSRA is a utility designed to convert graphical representations of chemical${Newline}structures into SMILES or SDF.${Newline}OSRA can read a document in any of the over 90 graphical formats parseable by${Newline}GraphicMagick and generate the SMILES or SDF representation of the molecular${Newline}structure images encountered within that document.${Newline}Authors:${Newline} ${common:Authors} common:Authors=Igor Filippov osra-2.1.3/package/linux/debian/rules.in0000664000175000017500000000247114115175251016611 0ustar igorigor#!/usr/bin/make -f # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 DPKG_EXPORT_BUILDFLAGS=1 include /usr/share/dpkg/buildflags.mk %: dh $@ override_dh_auto_configure: CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" dh_auto_configure -- --enable-docs --enable-lib --enable-java --with-tesseract --with-cuneiform --datadir='$${datarootdir}/$${PACKAGE_NAME}' --docdir='$${datarootdir}/doc/$${PACKAGE_NAME}' override_dh_install: # Check that *.install files have the same names as expected by debuild: [ -e debian/@PACKAGE_NAME@.install ] || @LN_S@ osra.install debian/@PACKAGE_NAME@.install [ -e debian/lib@PACKAGE_NAME@@LIB_MAJOR_VERSION@.install ] || @LN_S@ libosra.install debian/lib@PACKAGE_NAME@@LIB_MAJOR_VERSION@.install [ -e debian/lib@PACKAGE_NAME@-dev.install ] || @LN_S@ libosra-dev.install debian/lib@PACKAGE_NAME@-dev.install [ -e debian/lib@PACKAGE_NAME@-java@LIB_MAJOR_VERSION@.install ] || @LN_S@ libosra-java.install debian/lib@PACKAGE_NAME@-java@LIB_MAJOR_VERSION@.install # Continue with normal operation: dh_install # The default file "debian/substvars" is replaced by package-specific "debian/${package}.substvars", so in order not to mess with symlinks, we define an additional file with variable substitution: override_dh_gencontrol: dh_gencontrol -- -Tdebian/substvars osra-2.1.3/package/linux/debian/source/0000775000175000017500000000000014115175251016423 5ustar igorigorosra-2.1.3/package/linux/debian/source/format0000664000175000017500000000001414115175251017631 0ustar igorigor3.0 (quilt) osra-2.1.3/package/linux/debian/libosra-java.install0000664000175000017500000000003314115175251021061 0ustar igorigor/usr/lib/libosra_java.so* osra-2.1.3/package/linux/install.sh.in0000775000175000017500000000124614115175251016316 0ustar igorigor#!/bin/bash if (( $EUID != 0 )); then echo "The installation must be run as root" 1>&2 exit 1 fi mkdir -p /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@ || { echo "Cannot create /opt/local/@PACKAGE_NAME@ folder" 1>&2; exit; } mkdir -p /usr/local/bin || { echo "Cannot create /usr/local/bin folder" 1>&2; exit; } cp --remove-destination package/* /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@/ || { echo "Cannot copy to /opt/local/@PACKAGE_NAME@ folder" 1>&2; exit; } echo "Installing binary files in /opt/local/@PACKAGE_NAME@" cp --remove-destination osra /usr/local/bin || { echo "Cannot copy to /usr/local/bin" 1>&2; exit; } echo "Installing osra script in /usr/local/bin" osra-2.1.3/package/linux/osra-pdf0000775000175000017500000002461614115175252015354 0ustar igorigor#!/usr/bin/perl # This is a script to run OSRA processing on images and PDF files with higher quality output than what is produced with the default options, # It is expected to be useful mainly for PDF files. It works by running OSRA with multiple combinations of # image processing options and automatically selecting and filtering for the best possible molecular structure. Be prepared # that it is much slower than a regular OSRA run. You can modify the option set or the filtering criteria to better suit your needs. # OpenBabel perl binding are required to run this script. # Usage: ./osra-pdf input.pdf output.sdf # # Igor Filippov, igor.v.filippov@gmail.com $OSRA = "/usr/local/bin/osra"; @options = (['','-j'], ['', '-u 1','-u 2'], ['', '-r 150'] ); use List::Util qw[min max]; use Chemistry::OpenBabel; use Statistics::Descriptive; if ($#ARGV != 1) { print "Usage: ".$0." \n"; exit(1); } $TAN_MIN = 0.9; $DIFF = 0.1; $PREFIX = "/tmp/tmp.osra.output.".$$; $Chemistry::OpenBabel::obErrorLog->StopLogging(); $num_files = run_osra_with_different_options($OSRA,$ARGV[0],\@options,$PREFIX); $records = load_all_files($PREFIX,$num_files); $sdf_after_concat = concat_sd_files($records); $sdf_after_avg_bond = filter_by_avg_bond($sdf_after_concat); $sdf_after_props = filter_by_mol_properties($sdf_after_avg_bond); $sdf_after_filters = keep_unique_mols($sdf_after_props); save_sd_file($ARGV[1],$sdf_after_filters); for (my $i=0; $i<$num_files; $i++) { unlink($PREFIX.".".$i); } exit(0); sub run_osra_with_different_options($$$$) { my $osra = shift; my $input = shift; my $options = shift; my $prefix = shift; my @default_options = ($osra,$input,"-b","-c","-e","-p", "-f","sdf"); my $stop; my @current; my $num_files = 0; while (!$stop) { my @new_options = @default_options; for (my $i = 0; $i<=$#{$options}; $i++) { my $opt = $options->[$i]->[$current[$i]]; my @split_opt = split /\s+/,$opt; push(@new_options,@split_opt); } push (@new_options,'-w',$prefix.".".$num_files); system(@new_options); $num_files++; my $j = $#{$options}; while ($current[$j] == $#{$options->[$j]} && $j>=0) {$j--;} if ($j>=0) { $current[$j]++; for (my $k = $j+1; $k<=$#{$options}; $k++) { $current[$k] = 0; } } else { $stop = 1; } } return($num_files); } sub load_all_files($$) { my $prefix=shift; my $num_files = shift; my $obconversion = new Chemistry::OpenBabel::OBConversion; $obconversion->SetInFormat("sdf"); my $obmol = new Chemistry::OpenBabel::OBMol; my @records; # my $i=0; # for $file (@ARGV) for (my $i=0; $i<$num_files;$i++) { my $file = $prefix.".".$i; my $j=0; my $notatend = $obconversion->ReadFile($obmol, $file); while ($notatend) { $records[$i]->[$j++] = new Chemistry::OpenBabel::OBMol($obmol); $obmol->Clear(); $notatend = $obconversion->Read($obmol); } # $i++; } return(\@records); } sub concat_sd_files($) { my $records=shift; my @done; my @sdf_after_conctat; for (my $i1=0; $i1<=$#{$records}; $i1++) { for ($j1=0; $j1<= $#{$records->[$i1]}; $j1++) { if (!$done[$i1]->[$j1]) { my $sdf1=$records->[$i1]->[$j1]; my $obdata = $sdf1->GetData("Page"); my $page1 = $obdata->GetValue(); $obdata = $sdf1->GetData("Surrounding_box"); my $box1 = $obdata->GetValue(); $obdata = $sdf1->GetData("Confidence_estimate"); my $conf1 = $obdata->GetValue(); $box1 =~ /(\d+)x(\d+)\-(\d+)x(\d+)/; my $left1=$1; my $top1=$2; my $right1=$3; my $bottom1=$4; my $best_conf = $conf1; my $best_sdf=$sdf1; for (my $i2=$i1+1; $i2<=$#{$records}; $i2++) { for ($j2=0; $j2<= $#{$records->[$i2]}; $j2++) { if (!$done[$i2]->[$j2]) { my $sdf2=$records->[$i2]->[$j2]; my $obdata = $sdf2->GetData("Page"); my $page2 = $obdata->GetValue(); $obdata = $sdf2->GetData("Surrounding_box"); my $box2 = $obdata->GetValue(); $obdata = $sdf2->GetData("Confidence_estimate"); my $conf2 = $obdata->GetValue(); if ($page1 == $page2) { $box2 =~ /(\d+)x(\d+)\-(\d+)x(\d+)/; my $left2=$1; my $top2=$2; my $right2=$3; my $bottom2=$4; my $overlap = 0; my $l = max($left1, $left2); my $r = min($right1,$right2); my $t = max($top1, $top2); my $b = min($bottom1, $bottom2); if (($r > $l) && ($b > $t)) { $overlap = ($r-$l)*($b-$t); } my $area1=($right1-$left1)*($bottom1-$top1); my $area2=($right2-$left2)*($bottom2-$top2); if ($right1>$left1 && $right2>$left2 && $bottom1>$top1 && $bottom2>$top2) { my $tanimoto = $overlap/($area1+$area2-$overlap); my $x_rel_diff = abs(($right1-$left1)-($right2-$left2))/min(($right1-$left1),($right2-$left2)); my $y_rel_diff = abs(($bottom1-$top1)-($bottom2-$top2))/min(($bottom1-$top1),($bottom2-$top2)); if ($tanimoto > $TAN_MIN && $x_rel_diff<$DIFF && $y_rel_diff<$DIFF) { if ($conf2 > $best_conf) { $best_conf=$conf2; $best_sdf=$sdf2; } $done[$i2]->[$j2]=1; } } } } } } push(@sdf_after_concat,$best_sdf); $done[$i1]->[$j1]=1; } } } return(\@sdf_after_concat); } sub filter_by_avg_bond($) { my $sdf_after_concat=shift; my @arr; for (my $i=0; $i<=$#{$sdf_after_concat}; $i++) { my $sdf = $sdf_after_concat->[$i]; $obdata = $sdf->GetData("Confidence_estimate"); my $conf = $obdata->GetValue(); $obdata = $sdf->GetData("Average_bond_length"); my $avg_bond = $obdata->GetValue(); $arr[$i]->{'conf'}=$conf; $arr[$i]->{'avg'}=$avg_bond; } my $stat = Statistics::Descriptive::Full->new(); @sorted_arr = sort {$b->{'conf'} <=> $a->{'conf'}} @arr; for (my $i=0; $i<=min($#sorted_arr,max(10,$#sorted_arr/10)); $i++) { $stat->add_data($sorted_arr[$i]->{'avg'}); } my $mean_avg_bond = $stat->mean(); my $dev_avg_bond = $stat->standard_deviation(); my @sdf_after_avg_bond; for (my $i=0; $i<=$#{$sdf_after_concat}; $i++) { if ($arr[$i]->{'avg'} > $mean_avg_bond - 2*$dev_avg_bond && $arr[$i]->{'avg'} < $mean_avg_bond + 2*$dev_avg_bond) { push(@sdf_after_avg_bond,$sdf_after_concat->[$i]); } } return(\@sdf_after_avg_bond); } sub filter_by_mol_properties($) { my $sdf_after_avg_bond = shift; my @sdf_after_filters; my $obconversion = new Chemistry::OpenBabel::OBConversion; $obconversion->SetOutFormat("sdf"); for (my $i=0; $i<=$#{$sdf_after_avg_bond}; $i++) { my $sdf = $sdf_after_avg_bond->[$i]; my $obdata = $sdf->GetData("Confidence_estimate"); my $conf = $obdata->GetValue(); my $bad_atom_num=0; my $xx_present=0; my $num_bad_valence=0; my $min_bond_angle=180; my $max_carbon_bond_angle=0; my $min_exo_angle=180; for (my $j=1; $j<=$sdf->NumAtoms(); $j++) { my $atom = $sdf->GetAtom($j); my $anum = $atom->GetAtomicNum(); # count atoms not in C N O S F Cl Br I P if ($anum != 6 && $anum != 7 && $anum != 8 && $anum != 9 && $anum != 15 && $anum != 16 && $anum != 17 && $anum != 53 && $anum != 1 && $anum != 35) { $bad_atom_num++; } # is Xx present? my $sdf_string = $obconversion->WriteString($sdf); if ($sdf_string =~ "Xx") { $xx_present = 1; } if ($atom->GetValence() > $atom->GetImplicitValence()) { $num_bad_valence++; } my $iter = new Chemistry::OpenBabel::OBAtomAtomIter($atom); my @neighbor1; while (my $n = $iter->()) { push(@neighbor1,$n->GetIdx()); } for (my $k=0;$k<=$#neighbor1;$k++) { my $n = $sdf->GetAtom($neighbor1[$k]); my $iter2 = new Chemistry::OpenBabel::OBAtomAtomIter($n); my @neighbor2; while (my $n2 = $iter2->()) { push(@neighbor2,$n2->GetIdx()); } for (my $l=0; $l<=$#neighbor2; $l++) { my $n2 = $sdf->GetAtom($neighbor2[$l]); if ($neighbor2[$l] != $atom->GetIdx()) { my $angle = abs($sdf->GetAngle($atom,$n,$n2)); if ($angle < $min_bond_angle) { $min_bond_angle = $angle; } } if ($#neighbor1==0 && $n->IsInRing() && $n2->IsInRing()) { my $angle = abs($sdf->GetAngle($atom, $n, $n2)); if ($angle < $min_exo_angle) { $min_exo_angle=$angle; } } } } if ($atom->GetAtomicNum() == 6 && $#neighbor1 == 1) { my $atom1 = $sdf->GetAtom($neighbor1[0]); my $atom2 = $sdf->GetAtom($neighbor1[1]); my $bond1 = $sdf->GetBond($atom1,$atom); my $bond2 = $sdf->GetBond($atom2,$atom); if ($bond1->IsSingle() && $bond2->IsSingle()) { my $angle = abs($sdf->GetAngle($atom1, $atom, $atom2)); if ($angle > $max_carbon_bond_angle) { $max_carbon_bond_angle = $angle; } } } } my $min_bond_length = 10; my $num_bad_stereo = 0; for (my $j=0; $j<$sdf->NumBonds(); $j++) { my $bond = $sdf->GetBond($j); my $atom1 = $bond->GetBeginAtom(); my $atom2 = $bond->GetEndAtom(); if ($atom1->GetAtomicNum() != 1 && $atom2->GetAtomicNum() != 1 && $bond->GetLength() < $min_bond_length) { $min_bond_length = $bond->GetLength(); } if (!$atom1->IsChiral() && $bond->IsWedgeOrHash()) { $num_bad_stereo++; } } if ($conf>0.4 #&& $bad_atom_num==0 && $xx_present==0 && $min_bond_length>0.85 && $min_bond_angle>10 && $max_carbon_bond_angle<155 && $min_exo_angle>50 && $sdf->NumHvyAtoms()>6 && $sdf->GetMolWt()<2000 && $num_bad_stereo==0 && $num_bad_valence==0) { push(@sdf_after_filters,$sdf); } } return(\@sdf_after_filters); } sub keep_unique_mols($) { my $mols = shift; my $obconversion = new Chemistry::OpenBabel::OBConversion; $obconversion->SetOutFormat("inchi"); my %collection; my @unique; for (my $i=0; $i<=$#{$mols}; $i++) { my $sdf = $mols->[$i]; my $inchi = $obconversion->WriteString($sdf); if (!$inchi || !$collection{$inchi}) { push(@unique,$sdf); if ($inchi) { $collection{$inchi} = 1; } } } return(\@unique); } sub save_sd_file($$) { my $name = shift; my $sdf_after_filters = shift; my $obconversion = new Chemistry::OpenBabel::OBConversion; $obconversion->SetOutFormat("sdf"); open(OUT,">".$name); for (my $i=0; $i<=$#{$sdf_after_filters}; $i++) { my $sdf = $sdf_after_filters->[$i]; my $output = $obconversion->WriteString($sdf); print OUT $output; } close(OUT); } osra-2.1.3/package/linux/osra.pc.in0000664000175000017500000000034214115175251015575 0ustar igorigorprefix=@prefix@ exec_prefix=@exec_prefix@ Name: @PACKAGE_NAME@ Description: Chemical structure recognition library Version: @PACKAGE_VERSION@ Requires: gocr ocrad openbabel3 GraphicsMagick++ Libs: -l@libdir@ @LIBS@ Cflags: osra-2.1.3/package/linux/create_model_ga.py0000775000175000017500000001757514115175252017370 0ustar igorigor#!/usr/bin/python import sys import operator from operator import itemgetter from os import listdir from os.path import isfile, join import random import math from openbabel import * #verify_model = [-0.02285364975052934, 0.20014736496836238, 0.24359177367611942, 0.09879557707190285, 0.23658236605437208, 0.07640365396444981, -0.016708707126055426, 0.291372680023203, 0.19743466243758914, -0.04143248625528295, 0.13283691497820155, -0.09404435905499846, -0.34011551678018254, -0.036998642457270414, 0.3366565862477758, 0.2528949626509886, 0.3523503866589532, 0.3013887466571869, 0.2457272087651062, -0.08224552150295372, 0.0386321456632419, 0.2269247796030229, 0.191691888047917, 0.029364205782588967, -0.09207024117341821, 0.024551588143053422] verify_model = [-0.11469143725730054, 0.15723547931889853, 0.19765680222250673, 0.249101590474403, 0.1897669087341134, 0.19588348907301223, 0.3354622208036507, 0.16779269801176255, -0.21232000222198893, 0.016958281784354032, -0.08672059360133752, -0.05105752296619957, -0.349912750824004, 0.18836317536530647, 0.22316782354758827, 0.27741998968081166, 0.25710999274481955, 0.27968899280120096, 0.12695166847876285, -0.10020778884718293, 0.05150631410596443, 0.22283571763712148, 0.23130179826714167, 0.1049054095759948, 0.05333970810460394, -0.12491056666737535] #verify_model = [-0.1545855719726278, -0.16679291864636722, 0.2779073764931343, -0.183848833684335, 0.010790075194024773, 0.29094165316568404, 0.055324497605819506, -0.2104820104189514, -0.1781856691338483, 0.12170164214195042, -0.03319968208305941, -0.17050232311223057, -0.3855170942775288, -0.07088710430614285, 0.24005317771967355, 0.2759926472483148, 0.25348276233777095, 0.23427258354038655, -0.1175747967837222, -0.18681840394577787, 0.06103578120099978, 0.24422743725717977, 0.25207495568639754, -0.09625789745569688, -0.01025153552468599, 0.19182292957981223] #verify_model = [-0.18431997080588122, -0.13632503439995766, 0.2891372503939111, 0.169268288671698, 0.07457361791041998, 0.1896239880096002, 0.17921064798323905, 0.24807741146917148, -0.1415236210886208, 0.017500171104361622, -0.1444618582502517, -0.019936025471384962, -0.39685240156986173, -0.22908080638789957, 0.27059782240339336, 0.17386007711539425, 0.21106985232185135, 0.2865651377997317, 0.09715634915474097, 0.008962730235627716, 0.030950650868271857, 0.2256707621011711, 0.19237430308515277, -0.22938527889531524, 0.15229124226660562, 0.2099925925427031] def normalize(x): n = sum(map( operator.mul, x, x)) n = math.sqrt(n) y = [a/n for a in x] return y def trial_confidence(x,c): return sum(map( operator.mul, x, c)) def model_recall(N,res_iter_all,probabilities,target,inchi_list,total): recall_model = 0 k = 0 for i in range(N): total_probabilities = [0.,0.,0.,0.,0.] n_probabilities = [0.,0.,0.,0.,0.] recall_inchi = set() for r in range(0,5): for j in range(len(res_iter_all[i])): if (res_iter_all[i][j] == r): total_probabilities[r] += probabilities[k+j] n_probabilities[r] += 1 maxp = 0 maxr = 0 for r in range(0,5): if (n_probabilities[r]>0 and maxp < total_probabilities[r]/n_probabilities[r]): maxp = total_probabilities[r]/n_probabilities[r] maxr = r first = True for r in range(0,5): if (n_probabilities[r]>0 and maxp == total_probabilities[r]/n_probabilities[r] and (r == 2 or r == 3) and first): maxr = r first = False for j in range(len(res_iter_all[i])): if (res_iter_all[i][j] == maxr and target[k+j] == 1): recall_inchi.add(inchi_list[k+j]) k += len(res_iter_all[i]) recall_model += len(recall_inchi); return 1.*recall_model/total def mutation(n): d = [] for i in range(n): d.append(2.*random.random()-1.) return normalize(d) def crossover(population): n = len(population) i = random.randint(0,n-1) j = random.randint(0,n-1) v = population[i][1] u = population[j][1] m = random.randint(0,len(v)-1) r = v[:m] r.extend(u[m:]) return normalize(r) obconversion1 = OBConversion() obconversion1.SetInFormat("sdf") obconversion1.SetOutFormat("inchi") obmol1 = OBMol() obconversion2 = OBConversion() obconversion2.SetInFormat("sdf") obconversion2.SetOutFormat("inchi") obmol2 = OBMol() result = OBPlugin.ListAsString("fingerprints") assert "FP2" in result, result fingerprinter = OBFingerprint.FindFingerprint("FP2") v1 = vectorUnsignedInt() v2 = vectorUnsignedInt() obErrorLog.StopLogging() path1 = sys.argv[1] path2 = sys.argv[2] files = [ f for f in listdir(path1) if isfile(join(path1,f)) ] total = 0; recall = 0; target = [] train = [] confidence = [] resolutions = [] single = [] res_iter_all = [] probabilities = [] inchi_list = [] for f in files: file1 = join(path1,f); inchi_set1 = set() notatend1 = obconversion1.ReadFile(obmol1,file1) while notatend1: obmol1.AddHydrogens() inchi1 = obconversion1.WriteString(obmol1) if inchi1: inchi_set1.add(inchi1) total += 1; obmol1 = OBMol() notatend1 = obconversion1.Read(obmol1) file2 = join(path2,f); inchi_set2 = set() if isfile(file2): notatend2 = obconversion2.ReadFile(obmol2,file2) data_file = [] resolution = [] res_iter = [] while notatend2: obmol2.AddHydrogens() line = obmol2.GetData("Confidence_parameters").GetValue() data = [int(d) for d in line.split(",")] data_file.append(data) inchi2 = obconversion2.WriteString(obmol2) result = 0 if inchi2: inchi_set2.add(inchi2) if inchi2 in inchi_set1: result = 1 target.append(result) inchi_list.append(inchi2); resolution.append(int(obmol2.GetData("Resolution").GetValue())) res_iter.append(int(obmol2.GetData("Resolution_iteration").GetValue())) obmol2 = OBMol() notatend2 = obconversion2.Read(obmol2) resolutions.append(resolution) res_iter_all.append(res_iter) for d in data_file: train.append(d) recall += len(inchi_set1.intersection(inchi_set2)) N = len(files); ideal_recall = 1.*recall/total print "Ideal: ",ideal_recall population = [] if len(sys.argv)>3 and sys.argv[3] == "-verify": c = verify_model probabilities = [] for t in train: probabilities.append(trial_confidence(t,c)) r = model_recall(N,res_iter_all,probabilities,target,inchi_list,total) print "Model: ",r exit(0) for i in range(100): c = mutation(len(train[0])) probabilities = [] for t in train: probabilities.append(trial_confidence(t,c)) r = model_recall(N,res_iter_all,probabilities,target,inchi_list,total) population.append([r,c]) population.sort(key=itemgetter(0),reverse=True) round = 1 print round,population[0][0] while population[0][0] < ideal_recall - 0.001 and round < 1000: # keep top 10 new_population = population[0:10] # mutation 10 for i in range(10): c = mutation(len(train[0])) probabilities = [] for t in train: probabilities.append(trial_confidence(t,c)) r = model_recall(N,res_iter_all,probabilities,target,inchi_list,total) new_population.append([r,c]) # crossover 80 for i in range(80): c = crossover(population) probabilities = [] for t in train: probabilities.append(trial_confidence(t,c)) r = model_recall(N,res_iter_all,probabilities,target,inchi_list,total) new_population.append([r,c]) new_population.sort(key=itemgetter(0),reverse=True) population = new_population round += 1 print round,population[0][0] print population[0][1] osra-2.1.3/package/linux/plugins/0000775000175000017500000000000014115175251015362 5ustar igorigorosra-2.1.3/package/linux/plugins/bkchem/0000775000175000017500000000000014115175252016614 5ustar igorigorosra-2.1.3/package/linux/plugins/bkchem/convert_clipboard_image.xml0000664000175000017500000000074414115175252024204 0ustar igorigor Noel M. O'Boyle, Igor V. Filippov Takes an image of a molecule from the clipboard and converts it using OSRA (you need to install OSRA and set the environment variable OSRA to point to the executable) convert_clipboard_image.py Convert Image To Mol osra-2.1.3/package/linux/plugins/bkchem/convert_clipboard_image.py0000664000175000017500000000722014115175252024030 0ustar igorigor"""Authors: Noel O'Boyle and Igor V. Filippov Copied of......hmmm... Inspired by the "fetch from webbook" plugin :-) Converts an image from clipboard to a molecule using OSRA """ import os import popen2 import oasa_bridge import dialogs import tempfile import Pmw import StringIO import os, sys def err_mess_box(mess, title="Error"): #Pops up error OK-box message = "" for m in mess: message=message+m+"\n" dialog = Pmw.Dialog(App.paper, buttons=('OK',), defaultbutton='OK', title=title) w = Pmw.LabeledWidget(dialog.interior(), labelpos='n', label_text=message) w.pack(expand=1, fill='both', padx=4, pady=4) dialog.activate() def run_osra(osra): sdf = " " filedes, filename = tempfile.mkstemp(suffix='.png') if os.name=="posix": import pygtk pygtk.require('2.0') import gtk, gobject clipboard = gtk.clipboard_get() image=clipboard.wait_for_image() if not image: return sdf try: image.save(filename,"png") except: return sdf else: import ImageGrab image = ImageGrab.grabclipboard() if not image: return sdf try: image.save(filename) except: return sdf try: stdout, stdin, stderr = popen2.popen3('"%s" -f sdf %s' % (osra, filename)) except: os.remove(filename) return sdf sdf = stdout.read() #os.remove(filename) return sdf def present_mol(sdf): if not sdf.rstrip().endswith("$$$$"): return 0 try: mol = StringIO.StringIO(sdf) molec = oasa_bridge.read_molfile(mol, App.paper) mol.close() except: return 0 if len(molec.atoms)<2: return 0 averagey = sum([atom.y for atom in molec.atoms]) / float(len(molec.atoms)) for atom in molec.atoms: atom.y = 2*averagey - atom.y N = 0 for minimol in molec.get_disconnected_subgraphs(): N += 1 App.paper.stack.append(minimol) minimol.draw() App.paper.add_bindings() App.paper.start_new_undo_record() return N osra = os.environ.get("OSRA", None) if osra and os.path.isfile(osra): if os.name=="posix": r, w = os.pipe() # these are file descriptors, not file objects pid = os.fork() if pid: # we are the parent os.close(w) # use os.close() to close a file descriptor r = os.fdopen(r) # turn r into a file object dialog = dialogs.progress_dialog(App, title="Progress") dialog.update(0.1, top_text = "Calling OSRA...", bottom_text = "Image processing in progress") sdf = r.read() dialog.update(0.9, top_text = "Adding molecules to workspace...", bottom_text = "Almost there!") N = present_mol(sdf) dialog.close() if N<1: err_mess_box(["Image could not be converted to a molecule."]) # else: # err_mess_box(["%d molecule%s added" % (N, ["s", ""][N==1])], "Info") os.waitpid(pid, 0) # make sure the child process gets cleaned up else: # we are the child os.close(r) w = os.fdopen(w, 'w') sdf = run_osra(osra) w.write(sdf) w.close() sys.exit(0) else: dialog = dialogs.progress_dialog(App, title="Progress") dialog.update(0.1, top_text = "Calling OSRA...", bottom_text = "Image processing in progress") sdf = run_osra(osra) dialog.update(0.9, top_text = "Adding molecules to workspace...", bottom_text = "Almost there!") N = present_mol(sdf) dialog.close() if N<1: err_mess_box(["Image could not be converted to a molecule."]) # else: # err_mess_box(["%d molecule%s added" % (N, ["s", ""][N==1])], "Info") else: err_mess_box(["You need to set the environment variable " \ "OSRA to point to the OSRA executable.\n" \ "When setting the variable, do not include quotation " \ "marks around the path."]) osra-2.1.3/package/linux/INSTALL0000664000175000017500000000041214115175251014727 0ustar igorigorTo install run (as a root or via sudo) ./install.sh It will copy the contents of "package" into /opt/local/osra and the wrap-around shell script "osra" into /usr/local/bin Starting with version 2.1.0 Ghostscript is no longer necessary to process PDF and PS files. osra-2.1.3/package/linux/INSTALL-user0000664000175000017500000000026214115175251015706 0ustar igorigorTo install run ./install.sh It will copy the contents of "package" into $HOME/osra Starting with version 2.1.0 Ghostscript is no longer necessary to process PDF and PS files. osra-2.1.3/package/linux/osra.sh.in0000775000175000017500000000010214115175251015602 0ustar igorigor#!/bin/sh /opt/local/@PACKAGE_NAME@/@PACKAGE_VERSION@/osra-bin $* osra-2.1.3/configure0000775000175000017500000106015614115175251013067 0ustar igorigor#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for osra 2.1.3. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='osra' PACKAGE_TARNAME='osra' PACKAGE_VERSION='2.1.3' PACKAGE_STRING='osra 2.1.3' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS LIBOBJS PACKAGE_VERSION_WITH_DASHES resolved_datadir SHAREDEXT LDSHAREDFLAGS LIB_PATCH_VERSION GRAPHICS_MAGICK_CONFIG TESSERACT_LIB OSRA_JAVA EGREP GREP CXXCPP OSRA_LIB OPENMP_CXXFLAGS XSLTPROC NATURALDOCS RANLIB AR INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM LN_S RM HAVE_CXX11 OBJEXT EXEEXT ac_ct_CXX CPPFLAGS LDFLAGS CXXFLAGS CXX LD build_os build_vendor build_cpu build LIB_MINOR_VERSION LIB_MAJOR_VERSION target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_docs enable_openmp enable_static_linking enable_profiling with_macports enable_lib enable_java with_tclap_include with_potrace_include with_potrace_lib with_gocr_include with_gocr_lib with_ocrad_include with_ocrad_lib with_poppler_include with_poppler_lib with_tesseract_include with_tesseract with_tesseract_lib with_cuneiform with_cuneiform_include with_cuneiform_lib with_openbabel_include with_openbabel_lib enable_graphicsmagick_config with_graphicsmagick_include with_graphicsmagick_lib ' ac_precious_vars='build_alias host_alias target_alias CXX CXXFLAGS LDFLAGS LIBS CPPFLAGS CCC CXXCPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures osra 2.1.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/osra] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF System types: --build=BUILD configure for building on BUILD [guessed] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of osra 2.1.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-docs Enable generating of documentation (man pages, API reference, etc) --enable-openmp Enable OpenMP extensions for compilers that support it --disable-openmp do not use OpenMP --enable-static-linking Try to link all libraries statically (fallback to dynamic linking if statis was not found) (GCC-only) --enable-profiling Include verbose profiling/debugging information into the binary --enable-lib Include building of OSRA dynamic (.so) and static (.a) library into build cycle. --enable-java Include Java JNI bridge compilation into build cycle. Make sure you have JDK installed and JAVA_HOME environment variable pointing to its location or jni.h is in compiler search path. --disable-graphicsmagick-config Disable use of GraphicsMagick++-config for library linking Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-macports Alternative location for MacPorts libraries on OS X (default: /opt/local/lib) --with-tclap-include custom location of tclap/CmdLine.h from TCLAP (Templatized C++ Command Line Parser Library), see http://tclap.sourceforge.net/ (default: "auto") --with-potrace-include custom location of potracelib.h from POTRACE (vector tracing library), see http://potrace.sourceforge.net/ (default: "auto") --with-potrace-lib custom location of the library --with-gocr-include custom location of gocr/pgm2asc.h from GOCR (optical character recognition library), see http://jocr.sourceforge.net/ (default: "/usr/include/gocr /usr/local/include/gocr auto") --with-gocr-lib custom location of the library --with-ocrad-include custom location of ocradlib.h from OCRAD (optical character recognition program), see http://www.gnu.org/software/ocrad/ocrad.html (default: "auto") --with-ocrad-lib custom location of the library --with-poppler-include custom location of poppler/cpp/poppler-document.h from POPPLER, see http://poppler.freedesktop.org/ (default: "auto") --with-poppler-lib custom location of the library --with-tesseract-include Location of tesseract/baseapi.h header file --with-tesseract Enable optional support for Tesseract (OCR Engine developed at HP Labs), see http://code.google.com/p/tesseract-ocr/ (optional) (default: "auto") --with-tesseract-include Enable optional support for Tesseract (OCR Engine developed at HP Labs), see http://code.google.com/p/tesseract-ocr/ (default: "auto") --with-tesseract-lib custom location of the library --with-cuneiform Enable optional support for Cuneiform (OCR system developed by Cognitive technologies), see https://launchpad.net/cuneiform-linux (optional) (default: "auto") --with-cuneiform-include Enable optional support for Cuneiform (OCR system developed by Cognitive technologies), see https://launchpad.net/cuneiform-linux (default: "auto") --with-cuneiform-lib custom location of the library --with-openbabel-include custom location of openbabel/builder.h from OpenBabel (open source chemistry toolbox), see http://openbabel.sourceforge.net/wiki/Main_Page (default: "/usr/include/openbabel3 /usr/local/include/openbabel3") --with-openbabel-lib custom location of the library --with-graphicsmagick-include custom location of Magick++.h from GraphicsMagick (image manipulation library), see http://www.graphicsmagick.org/ (default: "/usr/include/GraphicsMagick /usr/local/include/GraphicsMagick") --with-graphicsmagick-lib custom location of the library Some influential environment variables: CXX C++ compiler command CXXFLAGS C++ compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXXCPP C++ preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF osra configure 2.1.3 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_check_header_mongrel LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_mongrel # ac_fn_cxx_try_run LINENO # ------------------------ # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_cxx_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_run # ac_fn_cxx_check_header_compile LINENO HEADER VAR INCLUDES # --------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_cxx_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_cxx_check_header_compile cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by osra $as_me 2.1.3, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu LIB_MAJOR_VERSION=2 LIB_MINOR_VERSION=1 ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac case "$build_os" in #( linux*) : build_os="linux" ;; #( darwin*) : build_os="darwin" ;; #( mingw*) : build_os="mingw" ;; #( *) : ;; esac ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu # Extract the first word of "ld", so it can be a program name with args. set dummy ld; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else case $LD in [\\/]* | ?:[\\/]*) ac_cv_path_LD="$LD" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_LD="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi LD=$ac_cv_path_LD if test -n "$LD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if ld ($LD) is GNU ld" >&5 $as_echo_n "checking if ld ($LD) is GNU ld... " >&6; } if "$LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then : $as_echo yes ac_gnu_ld=yes else $as_echo no ac_gnu_ld=no fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C++ compiler works" >&5 $as_echo_n "checking whether the C++ compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C++ compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler default output file name" >&5 $as_echo_n "checking for C++ compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C++ compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_cxx_compile_cxx11_required=false ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 $as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } if ${ax_cv_cxx_compile_cxx11+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_cxx_compile_cxx11=yes else ax_cv_cxx_compile_cxx11=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 $as_echo "$ax_cv_cxx_compile_cxx11" >&6; } if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi if test x$ac_success = xno; then for switch in -std=gnu++11 -std=gnu++0x; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" ac_success=yes break fi done fi if test x$ac_success = xno; then for switch in -std=c++11 -std=c++0x +std=c++11 "-h std=c++11"; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" ac_success=yes break fi done fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then HAVE_CXX11=0 { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 $as_echo "$as_me: No compiler with C++11 support was found" >&6;} else HAVE_CXX11=1 $as_echo "#define HAVE_CXX11 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler implements namespaces" >&5 $as_echo_n "checking whether the compiler implements namespaces... " >&6; } if ${ac_cv_cxx_namespaces+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ namespace Outer { namespace Inner { int i = 0; }} int main () { using namespace Outer::Inner; return i; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxx_namespaces=yes else ac_cv_cxx_namespaces=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_namespaces" >&5 $as_echo "$ac_cv_cxx_namespaces" >&6; } if test "$ac_cv_cxx_namespaces" = yes; then $as_echo "#define HAVE_NAMESPACES /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports Standard Template Library" >&5 $as_echo_n "checking whether the compiler supports Standard Template Library... " >&6; } if ${ac_cv_cxx_have_stl+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #ifdef HAVE_NAMESPACES using namespace std; #endif int main () { list x; x.push_back(5); list::iterator iter = x.begin(); if (iter != x.end()) ++iter; return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxx_have_stl=yes else ac_cv_cxx_have_stl=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_have_stl" >&5 $as_echo "$ac_cv_cxx_have_stl" >&6; } if test "$ac_cv_cxx_have_stl" = yes; then $as_echo "#define HAVE_STL /**/" >>confdefs.h fi if test "${ac_cv_cxx_have_stl}" != "yes"; then : as_fn_error $? "STL was not found; make sure you have installed libstdc++-dev package or check config.log" "$LINENO" 5 fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu # Extract the first word of "rm", so it can be a program name with args. set dummy rm; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_RM+:} false; then : $as_echo_n "(cached) " >&6 else case $RM in [\\/]* | ?:[\\/]*) ac_cv_path_RM="$RM" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_RM="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi RM=$ac_cv_path_RM if test -n "$RM"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RM" >&5 $as_echo "$RM" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' # Extract the first word of "ar", so it can be a program name with args. set dummy ar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_AR+:} false; then : $as_echo_n "(cached) " >&6 else case $AR in [\\/]* | ?:[\\/]*) ac_cv_path_AR="$AR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_AR="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi AR=$ac_cv_path_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi # Check whether --enable-docs was given. if test "${enable_docs+set}" = set; then : enableval=$enable_docs; # Extract the first word of "naturaldocs", so it can be a program name with args. set dummy naturaldocs; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_NATURALDOCS+:} false; then : $as_echo_n "(cached) " >&6 else case $NATURALDOCS in [\\/]* | ?:[\\/]*) ac_cv_path_NATURALDOCS="$NATURALDOCS" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_NATURALDOCS="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi NATURALDOCS=$ac_cv_path_NATURALDOCS if test -n "$NATURALDOCS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NATURALDOCS" >&5 $as_echo "$NATURALDOCS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "${NATURALDOCS}"; then : # Extract the first word of "NaturalDocs", so it can be a program name with args. set dummy NaturalDocs; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_NATURALDOCS+:} false; then : $as_echo_n "(cached) " >&6 else case $NATURALDOCS in [\\/]* | ?:[\\/]*) ac_cv_path_NATURALDOCS="$NATURALDOCS" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_NATURALDOCS="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi NATURALDOCS=$ac_cv_path_NATURALDOCS if test -n "$NATURALDOCS"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NATURALDOCS" >&5 $as_echo "$NATURALDOCS" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi # Extract the first word of "xsltproc", so it can be a program name with args. set dummy xsltproc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_XSLTPROC+:} false; then : $as_echo_n "(cached) " >&6 else case $XSLTPROC in [\\/]* | ?:[\\/]*) ac_cv_path_XSLTPROC="$XSLTPROC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_XSLTPROC="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi XSLTPROC=$ac_cv_path_XSLTPROC if test -n "$XSLTPROC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5 $as_echo "$XSLTPROC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "${XSLTPROC}"; then : as_fn_error $? "xsltproc executable was not found; make sure you have installed xsltproc package" "$LINENO" 5 fi fi if test -d "/usr/local/include"; then : CPPFLAGS="-I/usr/local/include ${CPPFLAGS}" fi if test -d "/usr/local/lib"; then : LDFLAGS="-L/usr/local/lib ${LDFLAGS}" fi if test -d "/opt/local/lib"; then : MACPORTSLIBS="/opt/local/lib" fi # Check whether --enable-openmp was given. if test "${enable_openmp+set}" = set; then : enableval=$enable_openmp; fi if test -z "${enable_openmp}" -a "$build_os" == "linux"; then : enable_openmp=yes fi if test "${enable_openmp}" == "yes"; then : OPENMP_CXXFLAGS= # Check whether --enable-openmp was given. if test "${enable_openmp+set}" = set; then : enableval=$enable_openmp; fi if test "$enable_openmp" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CXX option to support OpenMP" >&5 $as_echo_n "checking for $CXX option to support OpenMP... " >&6; } if ${ac_cv_prog_cxx_openmp+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef _OPENMP choke me #endif #include int main () { return omp_get_num_threads (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_prog_cxx_openmp='none needed' else ac_cv_prog_cxx_openmp='unsupported' for ac_option in -fopenmp -xopenmp -openmp -mp -omp -qsmp=omp -homp \ -Popenmp --openmp; do ac_save_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ac_option" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef _OPENMP choke me #endif #include int main () { return omp_get_num_threads (); } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_prog_cxx_openmp=$ac_option fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CXXFLAGS=$ac_save_CXXFLAGS if test "$ac_cv_prog_cxx_openmp" != unsupported; then break fi done fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_openmp" >&5 $as_echo "$ac_cv_prog_cxx_openmp" >&6; } case $ac_cv_prog_cxx_openmp in #( "none needed" | unsupported) ;; #( *) OPENMP_CXXFLAGS=$ac_cv_prog_cxx_openmp ;; esac fi fi # Check whether --enable-static-linking was given. if test "${enable_static_linking+set}" = set; then : enableval=$enable_static_linking; LDFLAGS="${LDFLAGS} -static-libgcc" if test "${ac_gnu_ld}" == "yes"; then : LDFLAGS="${LDFLAGS} -Wl,-static" fi fi # Check whether --enable-profiling was given. if test "${enable_profiling+set}" = set; then : enableval=$enable_profiling; LDFLAGS="-pg ${LDFLAGS}" CXXFLAGS="-Wall -g3 -pg ${CXXFLAGS}" else INSTALL_PROGRAM="${INSTALL_PROGRAM} -s" fi # Check whether --with-macports was given. if test "${with_macports+set}" = set; then : withval=$with_macports; MACPORTSLIBS="$withval" fi # Check whether --enable-lib was given. if test "${enable_lib+set}" = set; then : enableval=$enable_lib; OSRA_LIB=yes fi # Check whether --enable-java was given. if test "${enable_java+set}" = set; then : enableval=$enable_java; fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_cxx_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test "${enable_java}" == "yes"; then : if test -n "${JAVA_HOME}"; then : CPPFLAGS="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -I${JAVA_HOME}/include/win32 -I${JAVA_HOME}/include/darwin ${CPPFLAGS}" else CPPFLAGS="-I/usr/lib/jvm/default-java/include ${CPPFLAGS}" fi case "$build_os" in #( cygwin | mingw) : $as_echo "#define __int64 long long" >>confdefs.h ;; #( *) : ;; esac ac_fn_cxx_check_header_mongrel "$LINENO" "jni.h" "ac_cv_header_jni_h" "$ac_includes_default" if test "x$ac_cv_header_jni_h" = xyes; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { JNIEnv *j_env; jobjectArray j_obj_arr; jbyteArray j_byte_arr; jstring j_str; jboolean j_output_confidence; j_env->GetArrayLength(j_byte_arr); j_env->GetObjectArrayElement(j_obj_arr, 0); j_env->GetByteArrayElements(j_byte_arr, NULL); j_env->ReleaseByteArrayElements(j_byte_arr, (jbyte*) NULL, JNI_ABORT); j_env->GetStringUTFChars(j_str, NULL); j_env->ReleaseStringUTFChars(j_str, (const char *) NULL); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : OSRA_JAVA=yes else as_fn_error $? "Java JNI bridge support was requested, but failed because JNI API is not supported" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else as_fn_error $? "Java JNI bridge support was requested, but failed because \"jni.h\" was not found (search path: ${CPPFLAGS})" "$LINENO" 5 fi fi # Check whether --with-tclap-include was given. if test "${with_tclap_include+set}" = set; then : withval=$with_tclap_include; with_tclap="${with_tclap_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_tclap_include="auto" fi if test "${with_tclap_include}" == "no"; then : as_fn_error $? "The library tclap is obligatory. You cannot disable it." "$LINENO" 5 fi for ac_header in tclap/CmdLine.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tclap/CmdLine.h" "ac_cv_header_tclap_CmdLine_h" "$ac_includes_default" if test "x$ac_cv_header_tclap_CmdLine_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TCLAP_CMDLINE_H 1 _ACEOF ac_lib_tclap=yes else ac_lib_tclap=no fi done if test "${ac_lib_tclap}" != "yes" -a "${with_tclap}" != ""; then : for ax_var in tclap/CmdLine.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_tclap} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/tclap-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking tclap for tclap/CmdLine.h in ${ac_location}" >&5 $as_echo_n "checking tclap for tclap/CmdLine.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in tclap/CmdLine.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tclap/CmdLine.h" "ac_cv_header_tclap_CmdLine_h" "$ac_includes_default" if test "x$ac_cv_header_tclap_CmdLine_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TCLAP_CMDLINE_H 1 _ACEOF ac_lib_tclap=yes else ac_lib_tclap=no fi done if test "${ac_lib_tclap}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in tclap/CmdLine.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking tclap for tclap/CmdLine.h in ${ac_test_location}" >&5 $as_echo_n "checking tclap for tclap/CmdLine.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in tclap/CmdLine.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tclap/CmdLine.h" "ac_cv_header_tclap_CmdLine_h" "$ac_includes_default" if test "x$ac_cv_header_tclap_CmdLine_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TCLAP_CMDLINE_H 1 _ACEOF ac_lib_tclap=yes else ac_lib_tclap=no fi done if test "${ac_lib_tclap}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in tclap/CmdLine.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_tclap}" != "yes"; then : as_fn_error $? "tclap/CmdLine.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi # Check whether --with-potrace-include was given. if test "${with_potrace_include+set}" = set; then : withval=$with_potrace_include; with_potrace="${with_potrace_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_potrace="auto" fi if test "${with_potrace}" == "no"; then : as_fn_error $? "The library potrace is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-potrace-lib was given. if test "${with_potrace_lib+set}" = set; then : withval=$with_potrace_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in potracelib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "potracelib.h" "ac_cv_header_potracelib_h" "$ac_includes_default" if test "x$ac_cv_header_potracelib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POTRACELIB_H 1 _ACEOF ac_lib_potrace=yes else ac_lib_potrace=no fi done if test "${ac_lib_potrace}" != "yes" -a "${with_potrace}" != ""; then : for ax_var in potracelib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_potrace} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/potrace-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking potrace for potracelib.h in ${ac_location}" >&5 $as_echo_n "checking potrace for potracelib.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in potracelib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "potracelib.h" "ac_cv_header_potracelib_h" "$ac_includes_default" if test "x$ac_cv_header_potracelib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POTRACELIB_H 1 _ACEOF ac_lib_potrace=yes else ac_lib_potrace=no fi done if test "${ac_lib_potrace}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in potracelib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking potrace for potracelib.h in ${ac_test_location}" >&5 $as_echo_n "checking potrace for potracelib.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in potracelib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "potracelib.h" "ac_cv_header_potracelib_h" "$ac_includes_default" if test "x$ac_cv_header_potracelib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POTRACELIB_H 1 _ACEOF ac_lib_potrace=yes else ac_lib_potrace=no fi done if test "${ac_lib_potrace}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in potracelib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_potrace}" != "yes"; then : as_fn_error $? "potracelib.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi # Check whether --with-gocr-include was given. if test "${with_gocr_include+set}" = set; then : withval=$with_gocr_include; with_gocr="${with_gocr_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_gocr="/usr/include/gocr /usr/local/include/gocr auto" fi if test "${with_gocr}" == "no"; then : as_fn_error $? "The library gocr is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-gocr-lib was given. if test "${with_gocr_lib+set}" = set; then : withval=$with_gocr_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in pgm2asc.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "pgm2asc.h" "ac_cv_header_pgm2asc_h" "$ac_includes_default" if test "x$ac_cv_header_pgm2asc_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PGM2ASC_H 1 _ACEOF ac_lib_gocr=yes else ac_lib_gocr=no fi done if test "${ac_lib_gocr}" != "yes" -a "${with_gocr}" != ""; then : for ax_var in pgm2asc.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_gocr} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/gocr-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking gocr for pgm2asc.h in ${ac_location}" >&5 $as_echo_n "checking gocr for pgm2asc.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in pgm2asc.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "pgm2asc.h" "ac_cv_header_pgm2asc_h" "$ac_includes_default" if test "x$ac_cv_header_pgm2asc_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PGM2ASC_H 1 _ACEOF ac_lib_gocr=yes else ac_lib_gocr=no fi done if test "${ac_lib_gocr}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in pgm2asc.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking gocr for pgm2asc.h in ${ac_test_location}" >&5 $as_echo_n "checking gocr for pgm2asc.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in pgm2asc.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "pgm2asc.h" "ac_cv_header_pgm2asc_h" "$ac_includes_default" if test "x$ac_cv_header_pgm2asc_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PGM2ASC_H 1 _ACEOF ac_lib_gocr=yes else ac_lib_gocr=no fi done if test "${ac_lib_gocr}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in pgm2asc.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_gocr}" != "yes"; then : as_fn_error $? "pgm2asc.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi # Check whether --with-ocrad-include was given. if test "${with_ocrad_include+set}" = set; then : withval=$with_ocrad_include; with_ocrad="${with_ocrad_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_ocrad="auto" fi if test "${with_ocrad}" == "no"; then : as_fn_error $? "The library ocrad is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-ocrad-lib was given. if test "${with_ocrad_lib+set}" = set; then : withval=$with_ocrad_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in ocradlib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "ocradlib.h" "ac_cv_header_ocradlib_h" "$ac_includes_default" if test "x$ac_cv_header_ocradlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OCRADLIB_H 1 _ACEOF ac_lib_ocrad=yes else ac_lib_ocrad=no fi done if test "${ac_lib_ocrad}" != "yes" -a "${with_ocrad}" != ""; then : for ax_var in ocradlib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_ocrad} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/ocrad-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking ocrad for ocradlib.h in ${ac_location}" >&5 $as_echo_n "checking ocrad for ocradlib.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in ocradlib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "ocradlib.h" "ac_cv_header_ocradlib_h" "$ac_includes_default" if test "x$ac_cv_header_ocradlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OCRADLIB_H 1 _ACEOF ac_lib_ocrad=yes else ac_lib_ocrad=no fi done if test "${ac_lib_ocrad}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in ocradlib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking ocrad for ocradlib.h in ${ac_test_location}" >&5 $as_echo_n "checking ocrad for ocradlib.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in ocradlib.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "ocradlib.h" "ac_cv_header_ocradlib_h" "$ac_includes_default" if test "x$ac_cv_header_ocradlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OCRADLIB_H 1 _ACEOF ac_lib_ocrad=yes else ac_lib_ocrad=no fi done if test "${ac_lib_ocrad}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in ocradlib.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_ocrad}" != "yes"; then : as_fn_error $? "ocradlib.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi # Check whether --with-poppler-include was given. if test "${with_poppler_include+set}" = set; then : withval=$with_poppler_include; with_poppler="${with_poppler_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_poppler="auto" fi if test "${with_poppler}" == "no"; then : as_fn_error $? "The library poppler is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-poppler-lib was given. if test "${with_poppler_lib+set}" = set; then : withval=$with_poppler_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in poppler/cpp/poppler-document.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "poppler/cpp/poppler-document.h" "ac_cv_header_poppler_cpp_poppler_document_h" "$ac_includes_default" if test "x$ac_cv_header_poppler_cpp_poppler_document_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POPPLER_CPP_POPPLER_DOCUMENT_H 1 _ACEOF ac_lib_poppler=yes else ac_lib_poppler=no fi done if test "${ac_lib_poppler}" != "yes" -a "${with_poppler}" != ""; then : for ax_var in poppler/cpp/poppler-document.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_poppler} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/poppler-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking poppler for poppler/cpp/poppler-document.h in ${ac_location}" >&5 $as_echo_n "checking poppler for poppler/cpp/poppler-document.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in poppler/cpp/poppler-document.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "poppler/cpp/poppler-document.h" "ac_cv_header_poppler_cpp_poppler_document_h" "$ac_includes_default" if test "x$ac_cv_header_poppler_cpp_poppler_document_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POPPLER_CPP_POPPLER_DOCUMENT_H 1 _ACEOF ac_lib_poppler=yes else ac_lib_poppler=no fi done if test "${ac_lib_poppler}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in poppler/cpp/poppler-document.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking poppler for poppler/cpp/poppler-document.h in ${ac_test_location}" >&5 $as_echo_n "checking poppler for poppler/cpp/poppler-document.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in poppler/cpp/poppler-document.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "poppler/cpp/poppler-document.h" "ac_cv_header_poppler_cpp_poppler_document_h" "$ac_includes_default" if test "x$ac_cv_header_poppler_cpp_poppler_document_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_POPPLER_CPP_POPPLER_DOCUMENT_H 1 _ACEOF ac_lib_poppler=yes else ac_lib_poppler=no fi done if test "${ac_lib_poppler}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in poppler/cpp/poppler-document.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_poppler}" != "yes"; then : as_fn_error $? "poppler/cpp/poppler-document.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi # Check whether --with-tesseract-include was given. if test "${with_tesseract_include+set}" = set; then : withval=$with_tesseract_include; CPPFLAGS="-I${withval} ${CPPFLAGS}" fi # Check whether --with-tesseract was given. if test "${with_tesseract+set}" = set; then : withval=$with_tesseract; else with_tesseract="no" fi if test "${with_tesseract}" != "no"; then : if test "${with_tesseract}" == "" -o "${with_tesseract}" == "yes"; then : with_tesseract="auto" fi # Check whether --with-tesseract-include was given. if test "${with_tesseract_include+set}" = set; then : withval=$with_tesseract_include; with_tesseract="${with_tesseract_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_tesseract="auto" fi # Check whether --with-tesseract-lib was given. if test "${with_tesseract_lib+set}" = set; then : withval=$with_tesseract_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in tesseract/baseapi.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tesseract/baseapi.h" "ac_cv_header_tesseract_baseapi_h" "$ac_includes_default" if test "x$ac_cv_header_tesseract_baseapi_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TESSERACT_BASEAPI_H 1 _ACEOF ac_lib_tesseract=yes else ac_lib_tesseract=no fi done if test "${ac_lib_tesseract}" != "yes" -a "${with_tesseract}" != ""; then : for ax_var in tesseract/baseapi.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_tesseract} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/tesseract-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking tesseract for tesseract/baseapi.h in ${ac_location}" >&5 $as_echo_n "checking tesseract for tesseract/baseapi.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in tesseract/baseapi.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tesseract/baseapi.h" "ac_cv_header_tesseract_baseapi_h" "$ac_includes_default" if test "x$ac_cv_header_tesseract_baseapi_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TESSERACT_BASEAPI_H 1 _ACEOF ac_lib_tesseract=yes else ac_lib_tesseract=no fi done if test "${ac_lib_tesseract}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in tesseract/baseapi.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking tesseract for tesseract/baseapi.h in ${ac_test_location}" >&5 $as_echo_n "checking tesseract for tesseract/baseapi.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in tesseract/baseapi.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "tesseract/baseapi.h" "ac_cv_header_tesseract_baseapi_h" "$ac_includes_default" if test "x$ac_cv_header_tesseract_baseapi_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_TESSERACT_BASEAPI_H 1 _ACEOF ac_lib_tesseract=yes else ac_lib_tesseract=no fi done if test "${ac_lib_tesseract}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in tesseract/baseapi.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi fi # Check whether --with-cuneiform was given. if test "${with_cuneiform+set}" = set; then : withval=$with_cuneiform; else with_cuneiform="no" fi if test "${with_cuneiform}" != "no"; then : if test "${with_cuneiform}" == "" -o "${with_cuneiform}" == "yes"; then : with_cuneiform="auto" fi # Check whether --with-cuneiform-include was given. if test "${with_cuneiform_include+set}" = set; then : withval=$with_cuneiform_include; with_cuneiform="${with_cuneiform_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_cuneiform="auto" fi # Check whether --with-cuneiform-lib was given. if test "${with_cuneiform_lib+set}" = set; then : withval=$with_cuneiform_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in cuneiform.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "cuneiform.h" "ac_cv_header_cuneiform_h" "$ac_includes_default" if test "x$ac_cv_header_cuneiform_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CUNEIFORM_H 1 _ACEOF ac_lib_cuneiform=yes else ac_lib_cuneiform=no fi done if test "${ac_lib_cuneiform}" != "yes" -a "${with_cuneiform}" != ""; then : for ax_var in cuneiform.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_cuneiform} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/cuneiform-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking cuneiform for cuneiform.h in ${ac_location}" >&5 $as_echo_n "checking cuneiform for cuneiform.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in cuneiform.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "cuneiform.h" "ac_cv_header_cuneiform_h" "$ac_includes_default" if test "x$ac_cv_header_cuneiform_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CUNEIFORM_H 1 _ACEOF ac_lib_cuneiform=yes else ac_lib_cuneiform=no fi done if test "${ac_lib_cuneiform}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in cuneiform.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking cuneiform for cuneiform.h in ${ac_test_location}" >&5 $as_echo_n "checking cuneiform for cuneiform.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in cuneiform.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "cuneiform.h" "ac_cv_header_cuneiform_h" "$ac_includes_default" if test "x$ac_cv_header_cuneiform_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_CUNEIFORM_H 1 _ACEOF ac_lib_cuneiform=yes else ac_lib_cuneiform=no fi done if test "${ac_lib_cuneiform}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in cuneiform.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi fi # Check whether --with-openbabel-include was given. if test "${with_openbabel_include+set}" = set; then : withval=$with_openbabel_include; with_openbabel="${with_openbabel_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_openbabel="/usr/include/openbabel3 /usr/local/include/openbabel3" fi if test "${with_openbabel}" == "no"; then : as_fn_error $? "The library openbabel is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-openbabel-lib was given. if test "${with_openbabel_lib+set}" = set; then : withval=$with_openbabel_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF ac_lib_openbabel=yes else ac_lib_openbabel=no fi done if test "${ac_lib_openbabel}" != "yes" -a "${with_openbabel}" != ""; then : for ax_var in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_openbabel} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/openbabel-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking openbabel for openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h in ${ac_location}" >&5 $as_echo_n "checking openbabel for openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF ac_lib_openbabel=yes else ac_lib_openbabel=no fi done if test "${ac_lib_openbabel}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking openbabel for openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h in ${ac_test_location}" >&5 $as_echo_n "checking openbabel for openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF ac_lib_openbabel=yes else ac_lib_openbabel=no fi done if test "${ac_lib_openbabel}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_openbabel}" != "yes"; then : as_fn_error $? "openbabel/mol.h openbabel/obconversion.h openbabel/builder.h openbabel/alias.h openbabel/stereo/tetrahedral.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi osra_header_limits=yes ac_fn_cxx_check_header_mongrel "$LINENO" "limits.h" "ac_cv_header_limits_h" "$ac_includes_default" if test "x$ac_cv_header_limits_h" = xyes; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { int a = INT_MAX; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else osra_header_limits=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else osra_header_limits=no fi if test "${osra_header_limits}" != "yes"; then : as_fn_error $? "limits.h was not found; make sure you have installed libc-dev package or check config.log" "$LINENO" 5 fi osra_header_float=yes ac_fn_cxx_check_header_mongrel "$LINENO" "float.h" "ac_cv_header_float_h" "$ac_includes_default" if test "x$ac_cv_header_float_h" = xyes; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { float f = FLT_MAX; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else osra_header_float=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else osra_header_float=no fi if test "${osra_header_float}" != "yes"; then : as_fn_error $? "float.h was not found; make sure you have installed libc-dev package or check config.log" "$LINENO" 5 fi osra_header_libgen=yes ac_fn_cxx_check_header_mongrel "$LINENO" "libgen.h" "ac_cv_header_libgen_h" "$ac_includes_default" if test "x$ac_cv_header_libgen_h" = xyes; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { char *a = dirname(""); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else osra_header_libgen=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else osra_header_libgen=no fi if test "${osra_header_libgen}" != "yes"; then : as_fn_error $? "libgen.h was not found; make sure you have installed libc-dev package or check config.log" "$LINENO" 5 fi osra_lib_math=yes ac_fn_cxx_check_header_mongrel "$LINENO" "math.h" "ac_cv_header_math_h" "$ac_includes_default" if test "x$ac_cv_header_math_h" = xyes; then : if test "$build_os" != "darwin"; then : ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lm" >&5 $as_echo_n "checking for -lm... " >&6; } if ${ac_cv_lib_m+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in m do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { sqrt(1); fabs(1); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_m=yes else ac_cv_lib_m=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_m" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { sqrt(1); fabs(1); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_m=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m" >&5 $as_echo "$ac_cv_lib_m" >&6; } if test "x$ac_cv_lib_m" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in m do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_math=no fi fi else osra_lib_math=no fi if test "${osra_lib_math}" != "yes"; then : as_fn_error $? "math.h or libm was not found; make sure you have installed libc-dev package or check config.log" "$LINENO" 5 fi osra_lib_tclap=yes cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include // Defines the EOF constant; needed for MinGW #include #include int main () { TCLAP::CmdLine cmd(""); TCLAP::ValueArg a1("", "", "", false, 0, ""); cmd.add(a1); TCLAP::SwitchArg a2("", "", "", false); cmd.add(a2); cmd.parse(0, NULL); ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else osra_lib_tclap=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test "${osra_lib_tclap}" != "yes"; then : as_fn_error $? "TCLAP API check failed; make sure you have installed libtclap-dev package or check config.log" "$LINENO" 5 fi osra_lib_potrace=yes ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lpotrace" >&5 $as_echo_n "checking for -lpotrace... " >&6; } if ${ac_cv_lib_potrace+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in potrace do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern "C" { #include } int main () { potrace_state_t state; potrace_param_t param; potrace_bitmap_t bitmap; potrace_path_t path; potrace_dpoint_t dpoint; potrace_trace(¶m, &bitmap); potrace_state_free(&state); potrace_param_default(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_potrace=yes else ac_cv_lib_potrace=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_potrace" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern "C" { #include } int main () { potrace_state_t state; potrace_param_t param; potrace_bitmap_t bitmap; potrace_path_t path; potrace_dpoint_t dpoint; potrace_trace(¶m, &bitmap); potrace_state_free(&state); potrace_param_default(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_potrace=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_potrace" >&5 $as_echo "$ac_cv_lib_potrace" >&6; } if test "x$ac_cv_lib_potrace" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPOTRACE 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in potrace do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_potrace=no fi if test "${osra_lib_potrace}" != "yes"; then : as_fn_error $? "POTRACE API check failed; make sure you have installed libpotrace-dev package or check config.log" "$LINENO" 5 fi osra_lib_ocrad=yes ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -locrad" >&5 $as_echo_n "checking for -locrad... " >&6; } if ${ac_cv_lib_ocrad+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in ocrad do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct OCRAD_Pixmap opix; OCRAD_Descriptor * const ocrdes = OCRAD_open(); OCRAD_get_errno(ocrdes); OCRAD_set_image(ocrdes, &opix, 0); OCRAD_scale(ocrdes, 2); OCRAD_recognize(ocrdes, 0); OCRAD_result_first_character(ocrdes); OCRAD_close(ocrdes); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_ocrad=yes else ac_cv_lib_ocrad=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_ocrad" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { struct OCRAD_Pixmap opix; OCRAD_Descriptor * const ocrdes = OCRAD_open(); OCRAD_get_errno(ocrdes); OCRAD_set_image(ocrdes, &opix, 0); OCRAD_scale(ocrdes, 2); OCRAD_recognize(ocrdes, 0); OCRAD_result_first_character(ocrdes); OCRAD_close(ocrdes); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_ocrad=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ocrad" >&5 $as_echo "$ac_cv_lib_ocrad" >&6; } if test "x$ac_cv_lib_ocrad" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBOCRAD 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in ocrad do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_ocrad=no fi if test "${osra_lib_ocrad}" != "yes"; then : as_fn_error $? "OCRAD API check failed; make sure you have installed libocrad-dev package or check config.log" "$LINENO" 5 fi osra_lib_poppler=yes { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sem_init in -lpthread" >&5 $as_echo_n "checking for sem_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread_sem_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sem_init (); int main () { return sem_init (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_pthread_sem_init=yes else ac_cv_lib_pthread_sem_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_sem_init" >&5 $as_echo "$ac_cv_lib_pthread_sem_init" >&6; } if test "x$ac_cv_lib_pthread_sem_init" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" else ac_lib_poppler=no fi if test "${enable_static_linking:+set}" == "set" -o "$build_os" == "darwin"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflateEnd in -lz" >&5 $as_echo_n "checking for inflateEnd in -lz... " >&6; } if ${ac_cv_lib_z_inflateEnd+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inflateEnd (); int main () { return inflateEnd (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_z_inflateEnd=yes else ac_cv_lib_z_inflateEnd=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflateEnd" >&5 $as_echo "$ac_cv_lib_z_inflateEnd" >&6; } if test "x$ac_cv_lib_z_inflateEnd" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzlibVersion in -lbz2" >&5 $as_echo_n "checking for BZ2_bzlibVersion in -lbz2... " >&6; } if ${ac_cv_lib_bz2_BZ2_bzlibVersion+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lbz2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char BZ2_bzlibVersion (); int main () { return BZ2_bzlibVersion (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_bz2_BZ2_bzlibVersion=yes else ac_cv_lib_bz2_BZ2_bzlibVersion=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzlibVersion" >&5 $as_echo "$ac_cv_lib_bz2_BZ2_bzlibVersion" >&6; } if test "x$ac_cv_lib_bz2_BZ2_bzlibVersion" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBBZ2 1 _ACEOF LIBS="-lbz2 $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for png_create_read_struct in -lpng" >&5 $as_echo_n "checking for png_create_read_struct in -lpng... " >&6; } if ${ac_cv_lib_png_png_create_read_struct+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpng $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char png_create_read_struct (); int main () { return png_create_read_struct (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_png_png_create_read_struct=yes else ac_cv_lib_png_png_create_read_struct=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_png_png_create_read_struct" >&5 $as_echo "$ac_cv_lib_png_png_create_read_struct" >&6; } if test "x$ac_cv_lib_png_png_create_read_struct" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPNG 1 _ACEOF LIBS="-lpng $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiconv_open in -liconv" >&5 $as_echo_n "checking for libiconv_open in -liconv... " >&6; } if ${ac_cv_lib_iconv_libiconv_open+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-liconv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char libiconv_open (); int main () { return libiconv_open (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_iconv_libiconv_open=yes else ac_cv_lib_iconv_libiconv_open=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_iconv_libiconv_open" >&5 $as_echo "$ac_cv_lib_iconv_libiconv_open" >&6; } if test "x$ac_cv_lib_iconv_libiconv_open" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBICONV 1 _ACEOF LIBS="-liconv $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XML_ParserCreate in -lexpat" >&5 $as_echo_n "checking for XML_ParserCreate in -lexpat... " >&6; } if ${ac_cv_lib_expat_XML_ParserCreate+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lexpat $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char XML_ParserCreate (); int main () { return XML_ParserCreate (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_expat_XML_ParserCreate=yes else ac_cv_lib_expat_XML_ParserCreate=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_expat_XML_ParserCreate" >&5 $as_echo "$ac_cv_lib_expat_XML_ParserCreate" >&6; } if test "x$ac_cv_lib_expat_XML_ParserCreate" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBEXPAT 1 _ACEOF LIBS="-lexpat $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cmsSetLogErrorHandler in -llcms2" >&5 $as_echo_n "checking for cmsSetLogErrorHandler in -llcms2... " >&6; } if ${ac_cv_lib_lcms2_cmsSetLogErrorHandler+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-llcms2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cmsSetLogErrorHandler (); int main () { return cmsSetLogErrorHandler (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_lcms2_cmsSetLogErrorHandler=yes else ac_cv_lib_lcms2_cmsSetLogErrorHandler=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lcms2_cmsSetLogErrorHandler" >&5 $as_echo "$ac_cv_lib_lcms2_cmsSetLogErrorHandler" >&6; } if test "x$ac_cv_lib_lcms2_cmsSetLogErrorHandler" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBLCMS2 1 _ACEOF LIBS="-llcms2 $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TIFFFdOpen in -ltiff" >&5 $as_echo_n "checking for TIFFFdOpen in -ltiff... " >&6; } if ${ac_cv_lib_tiff_TIFFFdOpen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltiff $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char TIFFFdOpen (); int main () { return TIFFFdOpen (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_tiff_TIFFFdOpen=yes else ac_cv_lib_tiff_TIFFFdOpen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tiff_TIFFFdOpen" >&5 $as_echo "$ac_cv_lib_tiff_TIFFFdOpen" >&6; } if test "x$ac_cv_lib_tiff_TIFFFdOpen" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBTIFF 1 _ACEOF LIBS="-ltiff $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for jpeg_destroy_compress in -ljpeg" >&5 $as_echo_n "checking for jpeg_destroy_compress in -ljpeg... " >&6; } if ${ac_cv_lib_jpeg_jpeg_destroy_compress+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ljpeg $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char jpeg_destroy_compress (); int main () { return jpeg_destroy_compress (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_jpeg_jpeg_destroy_compress=yes else ac_cv_lib_jpeg_jpeg_destroy_compress=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jpeg_jpeg_destroy_compress" >&5 $as_echo "$ac_cv_lib_jpeg_jpeg_destroy_compress" >&6; } if test "x$ac_cv_lib_jpeg_jpeg_destroy_compress" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBJPEG 1 _ACEOF LIBS="-ljpeg $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for opj_image_destroy in -lopenjp2" >&5 $as_echo_n "checking for opj_image_destroy in -lopenjp2... " >&6; } if ${ac_cv_lib_openjp2_opj_image_destroy+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lopenjp2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char opj_image_destroy (); int main () { return opj_image_destroy (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_openjp2_opj_image_destroy=yes else ac_cv_lib_openjp2_opj_image_destroy=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_openjp2_opj_image_destroy" >&5 $as_echo "$ac_cv_lib_openjp2_opj_image_destroy" >&6; } if test "x$ac_cv_lib_openjp2_opj_image_destroy" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBOPENJP2 1 _ACEOF LIBS="-lopenjp2 $LIBS" else ac_lib_poppler=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for FT_Init_FreeType in -lfreetype" >&5 $as_echo_n "checking for FT_Init_FreeType in -lfreetype... " >&6; } if ${ac_cv_lib_freetype_FT_Init_FreeType+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lfreetype $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char FT_Init_FreeType (); int main () { return FT_Init_FreeType (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_freetype_FT_Init_FreeType=yes else ac_cv_lib_freetype_FT_Init_FreeType=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_freetype_FT_Init_FreeType" >&5 $as_echo "$ac_cv_lib_freetype_FT_Init_FreeType" >&6; } if test "x$ac_cv_lib_freetype_FT_Init_FreeType" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBFREETYPE 1 _ACEOF LIBS="-lfreetype $LIBS" else ac_lib_poppler=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for FcInit in -lfontconfig" >&5 $as_echo_n "checking for FcInit in -lfontconfig... " >&6; } if ${ac_cv_lib_fontconfig_FcInit+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lfontconfig $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char FcInit (); int main () { return FcInit (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_fontconfig_FcInit=yes else ac_cv_lib_fontconfig_FcInit=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fontconfig_FcInit" >&5 $as_echo "$ac_cv_lib_fontconfig_FcInit" >&6; } if test "x$ac_cv_lib_fontconfig_FcInit" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBFONTCONFIG 1 _ACEOF LIBS="-lfontconfig $LIBS" else ac_lib_poppler=no fi ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libs: poppler poppler-cpp" >&5 $as_echo_n "checking for libs: poppler poppler-cpp... " >&6; } if ${ac_cv_lib_poppler_poppler_cpp+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in poppler poppler-cpp do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { std::string input; poppler::document* doc = poppler::document::load_from_file(input); poppler::page_renderer r; int num_pages = doc->pages(); for (int i = 0; i < num_pages; i++) { poppler::page* p = doc->create_page(i); poppler::image im = r.render_page(p); const char *d = im.const_data(); } ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_poppler_poppler_cpp=yes else ac_cv_lib_poppler_poppler_cpp=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_poppler_poppler_cpp" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { std::string input; poppler::document* doc = poppler::document::load_from_file(input); poppler::page_renderer r; int num_pages = doc->pages(); for (int i = 0; i < num_pages; i++) { poppler::page* p = doc->create_page(i); poppler::image im = r.render_page(p); const char *d = im.const_data(); } ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_poppler_poppler_cpp=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_poppler_poppler_cpp" >&5 $as_echo "$ac_cv_lib_poppler_poppler_cpp" >&6; } if test "x$ac_cv_lib_poppler_poppler_cpp" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPOPPLER_POPPLER_CPP 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in poppler poppler-cpp do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_poppler=no fi if test "${osra_lib_poppler}" != "yes"; then : as_fn_error $? "POPPLER API check failed; make sure you have installed poppler-devel package or check config.log" "$LINENO" 5 fi if test "${ac_lib_tesseract}" == "yes"; then : if test "${enable_static_linking:+set}" == "set"; then : LIBS="-ljpeg -lz ${LIBS}" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sem_init in -lpthread" >&5 $as_echo_n "checking for sem_init in -lpthread... " >&6; } if ${ac_cv_lib_pthread_sem_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sem_init (); int main () { return sem_init (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_pthread_sem_init=yes else ac_cv_lib_pthread_sem_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_sem_init" >&5 $as_echo "$ac_cv_lib_pthread_sem_init" >&6; } if test "x$ac_cv_lib_pthread_sem_init" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" else ac_lib_tesseract=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TIFFGetField in -ltiff" >&5 $as_echo_n "checking for TIFFGetField in -ltiff... " >&6; } if ${ac_cv_lib_tiff_TIFFGetField+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ltiff $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char TIFFGetField (); int main () { return TIFFGetField (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_tiff_TIFFGetField=yes else ac_cv_lib_tiff_TIFFGetField=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tiff_TIFFGetField" >&5 $as_echo "$ac_cv_lib_tiff_TIFFGetField" >&6; } if test "x$ac_cv_lib_tiff_TIFFGetField" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBTIFF 1 _ACEOF LIBS="-ltiff $LIBS" else ac_lib_tesseract=no fi fi ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -ltesseract" >&5 $as_echo_n "checking for -ltesseract... " >&6; } if ${ac_cv_lib_tesseract+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in tesseract do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include using namespace tesseract; int main () { TessBaseAPI tess; tess.Init(NULL, ""); char* text = tess.TesseractRect((const unsigned char*) NULL, 1, 0, 0, 0, 0, 0); tess.End(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_tesseract=yes else ac_cv_lib_tesseract=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_tesseract" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include using namespace tesseract; int main () { TessBaseAPI tess; tess.Init(NULL, ""); char* text = tess.TesseractRect((const unsigned char*) NULL, 1, 0, 0, 0, 0, 0); tess.End(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_tesseract=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tesseract" >&5 $as_echo "$ac_cv_lib_tesseract" >&6; } if test "x$ac_cv_lib_tesseract" = xyes; then : $as_echo "#define HAVE_TESSERACT_LIB 1" >>confdefs.h TESSERACT_LIB=yes cat >>confdefs.h <<_ACEOF #define HAVE_LIBTESSERACT 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in tesseract do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else ac_lib_tesseract=no fi fi if test "${with_tesseract}" != "no" -a "${ac_lib_tesseract}" == "no"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Tesseract library support was requested, but disabled due to API check failure (see above messages for the reason or check config.log)" >&5 $as_echo "$as_me: WARNING: Tesseract library support was requested, but disabled due to API check failure (see above messages for the reason or check config.log)" >&2;} fi if test "${ac_lib_cuneiform}" == "yes"; then : ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lcuneiform" >&5 $as_echo_n "checking for -lcuneiform... " >&6; } if ${ac_cv_lib_cuneiform+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in cuneiform do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { PUMA_XOpen(NULL, ""); PUMA_XFinalRecognition(); PUMA_XClose(); PUMA_Init(0, NULL); PUMA_SetImportData(PUMA_Word32_Language, NULL); PUMA_SaveToMemory(NULL, PUMA_TOTEXT, PUMA_CODE_ASCII, NULL, 1); PUMA_Done(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_cuneiform=yes else ac_cv_lib_cuneiform=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_cuneiform" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { PUMA_XOpen(NULL, ""); PUMA_XFinalRecognition(); PUMA_XClose(); PUMA_Init(0, NULL); PUMA_SetImportData(PUMA_Word32_Language, NULL); PUMA_SaveToMemory(NULL, PUMA_TOTEXT, PUMA_CODE_ASCII, NULL, 1); PUMA_Done(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_cuneiform=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cuneiform" >&5 $as_echo "$ac_cv_lib_cuneiform" >&6; } if test "x$ac_cv_lib_cuneiform" = xyes; then : $as_echo "#define HAVE_CUNEIFORM_LIB 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define HAVE_LIBCUNEIFORM 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in cuneiform do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else ac_lib_cuneiform=no fi fi if test "${with_cuneiform}" != "no" -a "${ac_lib_cuneiform}" == "no"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cineiform library support was requested, but disabled due to API check failure (see above messages for the reason or check config.log)" >&5 $as_echo "$as_me: WARNING: Cineiform library support was requested, but disabled due to API check failure (see above messages for the reason or check config.log)" >&2;} fi osra_lib_openbabel=yes if test "$build_os" == "mingw"; then : LIBS="-lws2_32 ${LIBS}" fi if test "${enable_static_linking:+set}" == "set" -o "$build_os" == "darwin"; then : if test "$build_os" == "darwin"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GetINCHI in -linchi" >&5 $as_echo_n "checking for GetINCHI in -linchi... " >&6; } if ${ac_cv_lib_inchi_GetINCHI+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-linchi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char GetINCHI (); int main () { return GetINCHI (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_inchi_GetINCHI=yes else ac_cv_lib_inchi_GetINCHI=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inchi_GetINCHI" >&5 $as_echo "$ac_cv_lib_inchi_GetINCHI" >&6; } if test "x$ac_cv_lib_inchi_GetINCHI" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBINCHI 1 _ACEOF LIBS="-linchi $LIBS" fi LIBS="${MACPORTSLIBS}/libxml2.a ${MACPORTSLIBS}/libiconv.a ${MACPORTSLIBS}/libz.a ${LIBS}" else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GetINCHI in -linchi" >&5 $as_echo_n "checking for GetINCHI in -linchi... " >&6; } if ${ac_cv_lib_inchi_GetINCHI+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-linchi $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char GetINCHI (); int main () { return GetINCHI (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_inchi_GetINCHI=yes else ac_cv_lib_inchi_GetINCHI=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_inchi_GetINCHI" >&5 $as_echo "$ac_cv_lib_inchi_GetINCHI" >&6; } if test "x$ac_cv_lib_inchi_GetINCHI" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBINCHI 1 _ACEOF LIBS="-linchi $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inflateEnd in -lz" >&5 $as_echo_n "checking for inflateEnd in -lz... " >&6; } if ${ac_cv_lib_z_inflateEnd+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lz $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inflateEnd (); int main () { return inflateEnd (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_z_inflateEnd=yes else ac_cv_lib_z_inflateEnd=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_inflateEnd" >&5 $as_echo "$ac_cv_lib_z_inflateEnd" >&6; } if test "x$ac_cv_lib_z_inflateEnd" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBZ 1 _ACEOF LIBS="-lz $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for xmlParseFile in -lxml2" >&5 $as_echo_n "checking for xmlParseFile in -lxml2... " >&6; } if ${ac_cv_lib_xml2_xmlParseFile+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lxml2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char xmlParseFile (); int main () { return xmlParseFile (); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_xml2_xmlParseFile=yes else ac_cv_lib_xml2_xmlParseFile=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_xml2_xmlParseFile" >&5 $as_echo "$ac_cv_lib_xml2_xmlParseFile" >&6; } if test "x$ac_cv_lib_xml2_xmlParseFile" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBXML2 1 _ACEOF LIBS="-lxml2 $LIBS" fi fi fi ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lopenbabel" >&5 $as_echo_n "checking for -lopenbabel... " >&6; } if ${ac_cv_lib_openbabel+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in openbabel do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include #include int main () { OpenBabel::OBPairData label; OpenBabel::AliasData aliasData; OpenBabel::OBAtomIterator atom_iter; OpenBabel::OBAtom atom; OpenBabel::OBBond bond; OpenBabel::OBRing ring; OpenBabel::OBMol mol; OpenBabel::OBStereoFacade facade(&mol); OpenBabel::OBTetrahedralStereo stereo(&mol); OpenBabel::OBConversion conversion; label.SetAttribute(""); label.SetValue(""); aliasData.Expand(mol, 1); aliasData.SetAlias(""); aliasData.SetOrigin(OpenBabel::external); atom.Clear(); atom.GetAtomicNum(); atom.GetFormalCharge(); atom.GetId(); atom.GetParent(); atom.GetAtomicNum(); atom.IsChiral(); atom.IsHeteroatom(); atom.SetAtomicNum(0); atom.SetData(&aliasData); atom.SetFormalCharge(0); atom.SetIdx(0); atom.SetVector(0, 0, 0); bond.GetBeginAtom(); bond.GetBondOrder(); bond.GetFlags(); bond.GetBondOrder(); bond.IsHash(); bond.IsInRing(); // bond.IsTriple(); bond.IsWedge(); bond.SetBondOrder(0); // bond.UnsetAromatic(); // bond.UnsetDown(); // bond.UnsetHash(); // bond.UnsetUp(); // bond.UnsetWedge(); ring.IsAromatic(); mol.AddBond(0, 0, 0, 0); mol.AssignSpinMultiplicity(false); mol.BeginAtom(atom_iter); mol.BeginModify(); mol.ConnectTheDots(); std::vector< std::vector > v; mol.ContigFragList(v); mol.EndModify(); mol.FindRingAtomsAndBonds(); mol.GetAtom(0); mol.GetBond(0); mol.GetFirstAtom(); mol.GetSSSR(); mol.NewAtom(); mol.NextAtom(atom_iter); mol.NumAtoms(); mol.NumBonds(); mol.NumRotors(); mol.Separate(); mol.SetData(&label); mol.SetDimension(0); mol.StripSalts(0); facade.HasTetrahedralStereo(0); facade.GetTetrahedralStereo(0); stereo.GetConfig(); conversion.SetInFormat(""); conversion.SetOutFormat(""); conversion.ReadString(&mol, ""); conversion.Read(&mol); conversion.WriteString(&mol, false); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_openbabel=yes else ac_cv_lib_openbabel=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_openbabel" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include #include #include #include int main () { OpenBabel::OBPairData label; OpenBabel::AliasData aliasData; OpenBabel::OBAtomIterator atom_iter; OpenBabel::OBAtom atom; OpenBabel::OBBond bond; OpenBabel::OBRing ring; OpenBabel::OBMol mol; OpenBabel::OBStereoFacade facade(&mol); OpenBabel::OBTetrahedralStereo stereo(&mol); OpenBabel::OBConversion conversion; label.SetAttribute(""); label.SetValue(""); aliasData.Expand(mol, 1); aliasData.SetAlias(""); aliasData.SetOrigin(OpenBabel::external); atom.Clear(); atom.GetAtomicNum(); atom.GetFormalCharge(); atom.GetId(); atom.GetParent(); atom.GetAtomicNum(); atom.IsChiral(); atom.IsHeteroatom(); atom.SetAtomicNum(0); atom.SetData(&aliasData); atom.SetFormalCharge(0); atom.SetIdx(0); atom.SetVector(0, 0, 0); bond.GetBeginAtom(); bond.GetBondOrder(); bond.GetFlags(); bond.GetBondOrder(); bond.IsHash(); bond.IsInRing(); // bond.IsTriple(); bond.IsWedge(); bond.SetBondOrder(0); // bond.UnsetAromatic(); // bond.UnsetDown(); // bond.UnsetHash(); // bond.UnsetUp(); // bond.UnsetWedge(); ring.IsAromatic(); mol.AddBond(0, 0, 0, 0); mol.AssignSpinMultiplicity(false); mol.BeginAtom(atom_iter); mol.BeginModify(); mol.ConnectTheDots(); std::vector< std::vector > v; mol.ContigFragList(v); mol.EndModify(); mol.FindRingAtomsAndBonds(); mol.GetAtom(0); mol.GetBond(0); mol.GetFirstAtom(); mol.GetSSSR(); mol.NewAtom(); mol.NextAtom(atom_iter); mol.NumAtoms(); mol.NumBonds(); mol.NumRotors(); mol.Separate(); mol.SetData(&label); mol.SetDimension(0); mol.StripSalts(0); facade.HasTetrahedralStereo(0); facade.GetTetrahedralStereo(0); stereo.GetConfig(); conversion.SetInFormat(""); conversion.SetOutFormat(""); conversion.ReadString(&mol, ""); conversion.Read(&mol); conversion.WriteString(&mol, false); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_openbabel=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_openbabel" >&5 $as_echo "$ac_cv_lib_openbabel" >&6; } if test "x$ac_cv_lib_openbabel" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBOPENBABEL 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in openbabel do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_openbabel=no fi if test "${osra_lib_openbabel}" != "yes"; then : as_fn_error $? "OpenBabel API check failed; make sure you have installed libopenbabel-dev package or check config.log" "$LINENO" 5 fi # Check whether --enable-graphicsmagick-config was given. if test "${enable_graphicsmagick_config+set}" = set; then : enableval=$enable_graphicsmagick_config; fi if test "${enable_graphicsmagick_config:+set}" != "set"; then : # Extract the first word of "GraphicsMagick++-config", so it can be a program name with args. set dummy GraphicsMagick++-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_GRAPHICS_MAGICK_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $GRAPHICS_MAGICK_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_GRAPHICS_MAGICK_CONFIG="$GRAPHICS_MAGICK_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_GRAPHICS_MAGICK_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi GRAPHICS_MAGICK_CONFIG=$ac_cv_path_GRAPHICS_MAGICK_CONFIG if test -n "$GRAPHICS_MAGICK_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GRAPHICS_MAGICK_CONFIG" >&5 $as_echo "$GRAPHICS_MAGICK_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "${GRAPHICS_MAGICK_CONFIG}" == ""; then : as_fn_error $? "GraphicsMagick++-config was not found; make sure you have installed libgraphicsmagick++-dev package" "$LINENO" 5 fi CPPFLAGS="`${GRAPHICS_MAGICK_CONFIG} --cppflags` ${CPPFLAGS}" if test "${enable_static_linking:+set}" == "set" -o "$build_os" == "darwin"; then : LIBS="`${GRAPHICS_MAGICK_CONFIG} --libs` ${LIBS}" if test "$build_os" == "cygwin"; then : LIBS="-lxcb -lXau -lXdmcp ${LIBS}" fi fi else if test "${enable_static_linking:+set}" == "set" -o "$build_os" == "darwin"; then : LIBS="-ljpeg ${MACPORTSLIBS}/liblcms2.a ${MACPORTSLIBS}/libtiff.a ${MACPORTSLIBS}/libfreetype.a ${MACPORTSLIBS}/libjasper.a ${MACPORTSLIBS}/libpng.a ${MACPORTSLIBS}/libbz2.a ${MACPORTSLIBS}/libz.a ${LIBS}" fi fi $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" LIBS=\"${LIBS}\" after populated with GraphicsMagick++-config" >&5 # Check whether --with-graphicsmagick-include was given. if test "${with_graphicsmagick_include+set}" = set; then : withval=$with_graphicsmagick_include; with_graphicsmagick="${with_graphicsmagick_include}" CPPFLAGS="-I${withval} ${CPPFLAGS}" else with_graphicsmagick="/usr/include/GraphicsMagick /usr/local/include/GraphicsMagick" fi if test "${with_graphicsmagick}" == "no"; then : as_fn_error $? "The library graphicsmagick is obligatory. You cannot disable it." "$LINENO" 5 fi # Check whether --with-graphicsmagick-lib was given. if test "${with_graphicsmagick_lib+set}" = set; then : withval=$with_graphicsmagick_lib; LDFLAGS="-L${withval} ${LDFLAGS}" fi for ac_header in Magick++.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "Magick++.h" "ac_cv_header_Magickpp_h" "$ac_includes_default" if test "x$ac_cv_header_Magickpp_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_MAGICK___H 1 _ACEOF ac_lib_graphicsmagick=yes else ac_lib_graphicsmagick=no fi done if test "${ac_lib_graphicsmagick}" != "yes" -a "${with_graphicsmagick}" != ""; then : for ax_var in Magick++.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done for ac_test_location in ${with_graphicsmagick} do : if test "${ac_test_location}" = "auto"; then : for ac_location in `ls -1d $HOME/graphicsmagick-* 2>/dev/null | tac` do : if test -d "${ac_location}"; then : ax_probe_library_save_LDFLAGS=${LDFLAGS} ax_probe_library_save_CPPFLAGS=${CPPFLAGS} for ac_inc_location in `find "${ac_location}" -iname '*.h' | while read ac_include_location; do dirname "${ac_include_location}"; done | sort -u` do : CPPFLAGS="-I${ac_inc_location} ${CPPFLAGS}" done for ac_lib_location in `find "${ac_location}" -iname '*.so' -o -iname '*.sl' -o -iname '*.dylib' -o -iname '*.a' -o -iname '*.dll' | while read ac_library_location; do dirname "${ac_library_location}"; done | sort -u` do : LDFLAGS="-L${ac_lib_location} ${LDFLAGS}" done { $as_echo "$as_me:${as_lineno-$LINENO}: checking graphicsmagick for Magick++.h in ${ac_location}" >&5 $as_echo_n "checking graphicsmagick for Magick++.h in ${ac_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" and LDFLAGS=\"${LDFLAGS}\" for HOME location check" >&5 for ac_header in Magick++.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "Magick++.h" "ac_cv_header_Magickpp_h" "$ac_includes_default" if test "x$ac_cv_header_Magickpp_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_MAGICK___H 1 _ACEOF ac_lib_graphicsmagick=yes else ac_lib_graphicsmagick=no fi done if test "${ac_lib_graphicsmagick}" = "yes"; then : break 2 fi LDFLAGS=${ax_probe_library_save_LDFLAGS} CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in Magick++.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done else ax_probe_library_save_CPPFLAGS=${CPPFLAGS} CPPFLAGS="-I${ac_test_location} $CPPFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking graphicsmagick for Magick++.h in ${ac_test_location}" >&5 $as_echo_n "checking graphicsmagick for Magick++.h in ${ac_test_location}... " >&6; } $as_echo $as_echo "$as_me:${as_lineno-$LINENO}: CPPFLAGS=\"${CPPFLAGS}\" for custom location check" >&5 for ac_header in Magick++.h do : ac_fn_cxx_check_header_mongrel "$LINENO" "Magick++.h" "ac_cv_header_Magickpp_h" "$ac_includes_default" if test "x$ac_cv_header_Magickpp_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_MAGICK___H 1 _ACEOF ac_lib_graphicsmagick=yes else ac_lib_graphicsmagick=no fi done if test "${ac_lib_graphicsmagick}" = "yes"; then : break fi CPPFLAGS=${ax_probe_library_save_CPPFLAGS} for ax_var in Magick++.h do : as_ax_Var=`$as_echo "ac_cv_header_${ax_var}" | $as_tr_sh` { eval $as_ax_Var=; unset $as_ax_Var;} done fi done fi if test "${ac_lib_graphicsmagick}" != "yes"; then : as_fn_error $? "Magick++.h header(s) is missing. Check the default/listed above headers locations." "$LINENO" 5 fi osra_lib_graphicsmagick=yes ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libs: GraphicsMagick++ GraphicsMagick" >&5 $as_echo_n "checking for libs: GraphicsMagick++ GraphicsMagick... " >&6; } if ${ac_cv_lib_GraphicsMagickpp_GraphicsMagick+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in GraphicsMagick++ GraphicsMagick do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { Magick::Image image; Magick::Color color; Magick::ColorRGB colorRGB; Magick::ColorGray colorGray; image.getPixels(0, 0, 0, 0); image.pixelColor(0, 0); image.columns(); image.rows(); image.matte(); image.backgroundColor(color); image.rotate(0); color.alpha(); colorRGB.red(); colorRGB.green(); colorRGB.blue(); colorGray.shade(); MagickLib::InitializeMagick((char*) 0); MagickLib::DestroyMagick(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_GraphicsMagickpp_GraphicsMagick=yes else ac_cv_lib_GraphicsMagickpp_GraphicsMagick=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_GraphicsMagickpp_GraphicsMagick" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { Magick::Image image; Magick::Color color; Magick::ColorRGB colorRGB; Magick::ColorGray colorGray; image.getPixels(0, 0, 0, 0); image.pixelColor(0, 0); image.columns(); image.rows(); image.matte(); image.backgroundColor(color); image.rotate(0); color.alpha(); colorRGB.red(); colorRGB.green(); colorRGB.blue(); colorGray.shade(); MagickLib::InitializeMagick((char*) 0); MagickLib::DestroyMagick(); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_GraphicsMagickpp_GraphicsMagick=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_GraphicsMagickpp_GraphicsMagick" >&5 $as_echo "$ac_cv_lib_GraphicsMagickpp_GraphicsMagick" >&6; } if test "x$ac_cv_lib_GraphicsMagickpp_GraphicsMagick" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBGRAPHICSMAGICK___GRAPHICSMAGICK 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in GraphicsMagick++ GraphicsMagick do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_graphicsmagick=no fi if test "${osra_lib_graphicsmagick}" != "yes"; then : as_fn_error $? "GraphicsMagick API check failed; make sure you have installed libgraphicsmagick++-dev and all dependent -dev packages or check config.log" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pnm_readpnminit" >&5 $as_echo_n "checking for library containing pnm_readpnminit... " >&6; } if ${ac_cv_search_pnm_readpnminit+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pnm_readpnminit (); int main () { return pnm_readpnminit (); ; return 0; } _ACEOF for ac_lib in '' netpbm pnm; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_search_pnm_readpnminit=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_pnm_readpnminit+:} false; then : break fi done if ${ac_cv_search_pnm_readpnminit+:} false; then : else ac_cv_search_pnm_readpnminit=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pnm_readpnminit" >&5 $as_echo "$ac_cv_search_pnm_readpnminit" >&6; } ac_res=$ac_cv_search_pnm_readpnminit if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi osra_lib_gocr=yes ax_link_dynamically=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lPgm2asc" >&5 $as_echo_n "checking for -lPgm2asc... " >&6; } if ${ac_cv_lib_Pgm2asc+:} false; then : $as_echo_n "(cached) " >&6 else { LIBS_LIST=; unset LIBS_LIST;} for ax_var in Pgm2asc do : LIBS_LIST="-l${ax_var} ${LIBS_LIST}" done ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ax_try_link_save_LIBS=${LIBS} LIBS="${LIBS_LIST} ${LIBS}" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern "C" { #include #include } job_t *OCR_JOB, *JOB; int main () { job_t job; job_init(&job); job_init_image(&job); job_free_image(&job); pgm2asc(&job); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_Pgm2asc=yes else ac_cv_lib_Pgm2asc=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} if test "x$ac_cv_lib_Pgm2asc" = xno; then : if test "${enable_static_linking+set}" == "set" -a "${ac_gnu_ld}" == "yes"; then : ax_try_link_save_LIBS=${LIBS} LIBS="-Wl,-Bdynamic ${LIBS_LIST} -Wl,-static ${LIBS}" $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" for dynamic library presence check" >&5 cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ extern "C" { #include #include } job_t *OCR_JOB, *JOB; int main () { job_t job; job_init(&job); job_init_image(&job); job_free_image(&job); pgm2asc(&job); ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : ac_cv_lib_Pgm2asc=yes ax_link_dynamically=yes fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=${ax_try_link_save_LIBS} fi fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_Pgm2asc" >&5 $as_echo "$ac_cv_lib_Pgm2asc" >&6; } if test "x$ac_cv_lib_Pgm2asc" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPGM2ASC 1 _ACEOF if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-static ${LIBS}" fi for ax_var in Pgm2asc do : (echo "${LIBS}" | grep -q -- "-l${ax_var} ") || LIBS="-l${ax_var} ${LIBS}" done if test "${ax_link_dynamically}" == "yes"; then : LIBS="-Wl,-Bdynamic ${LIBS}" fi $as_echo "$as_me:${as_lineno-$LINENO}: LIBS=\"${LIBS}\" after linking check succeeded" >&5 else osra_lib_gocr=no fi if test "${osra_lib_gocr}" != "yes"; then : as_fn_error $? "GOCR API check failed; make sure you have installed libgocr-dev package or check config.log" "$LINENO" 5 fi LIB_PATCH_VERSION=`echo ${PACKAGE_VERSION} | perl -ne '/(\d+)\.(\d+)\.(\d+)/ || die; printf "%d%02d%02d", $1, $2, $3;'` case "$build_os" in #( darwin) : LDSHAREDFLAGS='-dynamiclib -Wl,-dylib_install_name -Wl,$@.$(LIB_MAJOR_VERSION)' SHAREDEXT=.dylib ;; #( cygwin | mingw) : LDSHAREDFLAGS='-shared -Wl,--kill-at -Wl,--out-implib,$@.a' SHAREDEXT=.dll ;; #( *) : LDSHAREDFLAGS='-shared -Wl,-soname,$@.$(LIB_MAJOR_VERSION)' SHAREDEXT=.so ;; #( *) : ;; esac if test -n "${OPENMP_CXXFLAGS}" ; then : CPPFLAGS="${OPENMP_CFLAGS} ${CPPFLAGS}" CXXFLAGS="${OPENMP_CXXFLAGS} ${CXXFLAGS}" fi test "$prefix" = "NONE" && prefix=$ac_default_prefix datadir=`eval echo "${datadir}"` docdir=`eval echo "${docdir}"` resolved_datadir=`eval echo "${datadir}"` cat >>confdefs.h <<_ACEOF #define DATA_DIR "$resolved_datadir" _ACEOF resolved_datadir=$resolved_datadir PACKAGE_VERSION_WITH_DASHES=`echo ${PACKAGE_VERSION} | perl -ne '/(\d+)\.(\d+)\.(\d+)/ || die; printf "%d-%d-%d", $1, $2, $3;'` ac_config_headers="$ac_config_headers src/config.h" ac_config_files="$ac_config_files Makefile Makefile.inc doc/manual.sgml package/linux/osra.pc package/linux/debian/control package/linux/debian/rules package/linux/suse/osra.spec pom.xml package/linux/osra.sh package/linux/install.sh package/linux/install-user.sh package/osx/osra.sh package/osx/install.sh package/win32/osra.nsi" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by osra $as_me 2.1.3, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ osra config.status 2.1.3 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "Makefile.inc") CONFIG_FILES="$CONFIG_FILES Makefile.inc" ;; "doc/manual.sgml") CONFIG_FILES="$CONFIG_FILES doc/manual.sgml" ;; "package/linux/osra.pc") CONFIG_FILES="$CONFIG_FILES package/linux/osra.pc" ;; "package/linux/debian/control") CONFIG_FILES="$CONFIG_FILES package/linux/debian/control" ;; "package/linux/debian/rules") CONFIG_FILES="$CONFIG_FILES package/linux/debian/rules" ;; "package/linux/suse/osra.spec") CONFIG_FILES="$CONFIG_FILES package/linux/suse/osra.spec" ;; "pom.xml") CONFIG_FILES="$CONFIG_FILES pom.xml" ;; "package/linux/osra.sh") CONFIG_FILES="$CONFIG_FILES package/linux/osra.sh" ;; "package/linux/install.sh") CONFIG_FILES="$CONFIG_FILES package/linux/install.sh" ;; "package/linux/install-user.sh") CONFIG_FILES="$CONFIG_FILES package/linux/install-user.sh" ;; "package/osx/osra.sh") CONFIG_FILES="$CONFIG_FILES package/osx/osra.sh" ;; "package/osx/install.sh") CONFIG_FILES="$CONFIG_FILES package/osx/install.sh" ;; "package/win32/osra.nsi") CONFIG_FILES="$CONFIG_FILES package/win32/osra.nsi" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi { $as_echo "$as_me:${as_lineno-$LINENO}: Now you can run \"make all install\" to compile and install the application." >&5 $as_echo "$as_me: Now you can run \"make all install\" to compile and install the application." >&6;} osra-2.1.3/src/0000775000175000017500000000000014115175251011736 5ustar igorigorosra-2.1.3/src/osra_fragments.cpp0000664000175000017500000001434614115175251015464 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File osra_fragments.cpp // // Defines operations on molecular fragments // #include // FLT_MAX #include // INT_MAX #include // std::ostream, std::cout #include "osra.h" #include "osra_common.h" #include "osra_fragments.h" double atom_distance(const std::vector &atom, int a, int b) { return (distance(atom[a].x, atom[a].y, atom[b].x, atom[b].y)); } /** * TODO: Returning the vector from the stack causes copy constructor to trigger, which is inefficient. * Consider passing the vector as a reference. */ std::vector > find_fragments( const std::vector &bond, int n_bond, const std::vector &atom) { std::vector > frags; std::vector pool; int n = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists && atom[bond[i].a].exists && atom[bond[i].b].exists) pool.push_back(i); while (!pool.empty()) { frags.resize(n + 1); frags[n].push_back(bond[pool.back()].a); frags[n].push_back(bond[pool.back()].b); pool.pop_back(); bool found = true; while (found) { found = false; unsigned int i = 0; while (i < pool.size()) { bool found_a = false; bool found_b = false; bool newfound = false; for (unsigned int k = 0; k < frags[n].size(); k++) { if (frags[n][k] == bond[pool[i]].a) found_a = true; else if (frags[n][k] == bond[pool[i]].b) found_b = true; } if (found_a && !found_b) { frags[n].push_back(bond[pool[i]].b); pool.erase(pool.begin() + i); found = true; newfound = true; } if (!found_a && found_b) { frags[n].push_back(bond[pool[i]].a); pool.erase(pool.begin() + i); found = true; newfound = true; } if (found_a && found_b) { pool.erase(pool.begin() + i); newfound = true; } if (!newfound) i++; } } n++; } return (frags); } int reconnect_fragments(std::vector &bond, int n_bond, std::vector &atom, double avg) { std::vector > frags = find_fragments(bond, n_bond, atom); if (frags.size() <= 3) { for (unsigned int i = 0; i < frags.size(); i++) if (frags[i].size() > 2) for (unsigned int j = i + 1; j < frags.size(); j++) if (frags[j].size() > 2) { double l = FLT_MAX; int atom1 = 0, atom2 = 0; for (unsigned int ii = 0; ii < frags[i].size(); ii++) for (unsigned int jj = 0; jj < frags[j].size(); jj++) { double d = atom_distance(atom, frags[i][ii], frags[j][jj]); if (d < l) { l = d; atom1 = frags[i][ii]; atom2 = frags[j][jj]; } } if (l < 1.1 * avg && l > avg / 3) { //cout< populate_fragments( const std::vector > &frags, const std::vector &atom) { std::vector r; for (unsigned int i = 0; i < frags.size(); i++) { fragment_t f; f.x1 = INT_MAX; f.x2 = 0; f.y1 = INT_MAX; f.y2 = 0; for (unsigned j = 0; j < frags[i].size(); j++) { f.atom.push_back(frags[i][j]); if (atom[frags[i][j]].min_x < f.x1) f.x1 = atom[frags[i][j]].min_x; if (atom[frags[i][j]].max_x > f.x2) f.x2 = atom[frags[i][j]].max_x; if (atom[frags[i][j]].min_y < f.y1) f.y1 = atom[frags[i][j]].min_y; if (atom[frags[i][j]].max_y > f.y2) f.y2 = atom[frags[i][j]].max_y; //cout<<"Atoms2: "< bb.y2) return (false); if (aa.x1 > bb.x1) return (false); if (aa.x1 < bb.x1) return (true); return (false); } osra-2.1.3/src/config.h.in0000664000175000017500000000300114115175251013753 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ /* Tell CImg library that there is not going to be a X11-capable display attached. It doesn't matter for Linux hosts, but matters for OS X. */ #define cimg_display_type 0 /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define the location of data files. */ #undef DATA_DIR /* Is tesseract library present? */ #undef HAVE_TESSERACT_LIB /* Is cuneiform library present? */ #undef HAVE_CUNEIFORM_LIB osra-2.1.3/src/osra_grayscale.cpp0000664000175000017500000002433014115175251015442 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File: osra_grayscale.cpp // // Defines grayscale conversion functions // #include #include // std::ostream, std::cout #include // fabs(double) #include "osra.h" #include "osra_grayscale.h" const Color getBgColor(const Image &image) { ColorGray c, r; r = image.pixelColor(1, 1); for (int i = 0; i < BG_PICK_POINTS; i++) { double a = (double)rand()/RAND_MAX; double b = (double)rand()/RAND_MAX; int x = int(image.columns() *a); int y = int(image.rows() * b); c = image.pixelColor(x, y); if (c.shade() > r.shade()) r = c; } return (r); } void otsu_find_peaks(const std::vector &h, int num_bins, int &peak1, int &peak2, int &max1, int &max2) { // Otsu Algorithm, from http://habrahabr.ru/blogs/algorithm/112079/ unsigned int m = 0; unsigned int n = 0; double maxSigma = -1; unsigned int min_t = num_bins; unsigned int alpha1 = 0; unsigned int beta1 = 0; for (unsigned int i = 0; i maxSigma) { maxSigma = sigma; min_t = t; } } max1 = 0; peak1 = 0; for (unsigned int i = 0; i<=min_t; i++) if (h[i] > max1) { max1 = h[i]; peak1 = i; } max2 = 0; peak2 = 0; for (unsigned int i = min_t+1; i max2) { max2 = h[i]; peak2 = i; } } Image adaptive_otsu(const Image &image, int window) { int num_bins=20; Image result(Geometry(image.columns(),image.rows()),"white"); std::vector h(num_bins,0); std::vector h0(num_bins,0); ColorGray g; int peak1, peak2, max1, max2; for (int i1 = 0; i1 < std::min((int)image.columns(), window/2); i1++) for (int j1 = 0; j1 < std::min((int)image.rows(), window/2); j1++) { g = image.pixelColor(i1, j1); h0[int((num_bins-1)*g.shade())]++; } for (int j = 0; j < image.rows(); j++) { for (int k = 0; k median) result.pixelColor(i,j,"white"); else result.pixelColor(i,j,"black"); if ((i-window/2) >=0) for (int j1 = std::max(0, j-window/2); j1 < std::min((int)image.rows(), j + window/2); j1++) { g = image.pixelColor(i-window/2, j1); h[int((num_bins-1)*g.shade())]--; } if ((i+window/2) < image.columns()) for (int j1 = std::max(0, j-window/2); j1 < std::min((int)image.rows(), j + window/2); j1++) { g = image.pixelColor(i+window/2, j1); h[int((num_bins-1)*g.shade())]++; } } if ((j-window/2) >=0) for (int i1 = 0; i1 < std::min((int)image.columns(), window/2); i1++) { g = image.pixelColor(i1,j-window/2); h0[int((num_bins-1)*g.shade())]--; } if ((j+window/2) < image.rows()) for (int i1 = 0; i1 < std::min((int)image.columns(), window/2); i1++) { g = image.pixelColor(i1,j+window/2); h0[int((num_bins-1)*g.shade())]++; } } return(result); } // https://habrahabr.ru/post/278435/ Image Bradley_threshold(const Image &src) { int width = src.columns(); int height = src.rows(); Image res(Geometry(width, height),"white"); const int S = width/8; int s2 = S/2; const float t = 0.15; unsigned long* integral_image = 0; long sum=0; int count=0; int index; int x1, y1, x2, y2; ColorGray g; integral_image = new unsigned long [width*height*sizeof(unsigned long*)]; for (int i = 0; i < width; i++) { sum = 0; for (int j = 0; j < height; j++) { index = j * width + i; g = src.pixelColor(i, j); sum += g.shade() * 255; if (i==0) integral_image[index] = sum; else integral_image[index] = integral_image[index-1] + sum; } } for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { index = j * width + i; x1=i-s2; x2=i+s2; y1=j-s2; y2=j+s2; if (x1 < 0) x1 = 0; if (x2 >= width) x2 = width-1; if (y1 < 0) y1 = 0; if (y2 >= height) y2 = height-1; count = (x2-x1)*(y2-y1); sum = integral_image[y2*width+x2] - integral_image[y1*width+x2] - integral_image[y2*width+x1] + integral_image[y1*width+x1]; g = src.pixelColor(i, j); if ((long)(g.shade() * 255 * count) < (long)(sum*(1.0-t))) res.pixelColor(i,j,"black"); else res.pixelColor(i,j,"white"); } } delete[] integral_image; return res; } bool convert_to_gray(Image &image, bool invert, bool adaptive, bool verbose) { int num_bins=50; int num_bins_rgb = 20; std::vector h(num_bins,0); std::vector > > bg_search(num_bins_rgb, std::vector > (num_bins_rgb, std::vector(num_bins_rgb, 0))); ColorRGB c,b; Color t; ColorGray g; double a; image.type(TrueColorMatteType); for (int i = 0; i < BG_PICK_POINTS; i++) { double a = (double) rand() / RAND_MAX; double b = (double) rand() / RAND_MAX; int x = int(image.columns() * a); int y = int(image.rows() * b); c = image.pixelColor(x, y); bg_search[int((num_bins_rgb-1)*c.red())][int((num_bins_rgb-1)*c.green())][int((num_bins_rgb-1)*c.blue())]++; } int bg_peak = 0; double bg_pos_red = 0, bg_pos_green = 0, bg_pos_blue = 0; for (int i=0; i bg_peak) { bg_peak = bg_search[i][j][k]; bg_pos_red = (double)i/(num_bins_rgb-1); bg_pos_green = (double)j/(num_bins_rgb-1); bg_pos_blue = (double)k/(num_bins_rgb-1); } bool color_background = false; if (verbose) { std::cout << "Background rgb: " << bg_pos_red << " " << bg_pos_green << " " << bg_pos_blue << std::endl; } if (fabs(bg_pos_red-bg_pos_green) > 0.05 || fabs(bg_pos_red-bg_pos_blue)>0.05 || fabs(bg_pos_green-bg_pos_blue)>0.05) color_background = true; bool matte = image.matte(); if (color_background) { image.contrast(2); image.type(GrayscaleType); } for (unsigned int i = 0; i < image.columns(); i++) for (unsigned int j = 0; j < image.rows(); j++) { t = image.pixelColor(i, j); b = t; g = t; if (matte && t.alpha() == 1 && g.shade() < 0.5) { g.shade(1); image.pixelColor(i, j, g); } else if (!color_background && (fabs(b.red()-b.green()) > 0.1 || fabs(b.red()-b.blue()) > 0.1 || fabs(b.blue()-b.green()) > 0.1)) { if (fabs(b.red()-bg_pos_red) >= fabs(b.green()-bg_pos_green) && fabs(b.red()-bg_pos_red) >= fabs(b.blue()-bg_pos_blue)) a = b.red(); else if (fabs(b.red()-bg_pos_red) < fabs(b.green()-bg_pos_green) && fabs(b.green()-bg_pos_green) >= fabs(b.blue()-bg_pos_blue)) a = b.green(); else a = b.blue(); c.red(a); c.green(a); c.blue(a); image.pixelColor(i, j, c); } g = image.pixelColor(i, j); h[int((num_bins-1)*g.shade())]++; } int peak1, peak2, max1, max2; otsu_find_peaks(h,num_bins,peak1,peak2, max1, max2); double distance_between_peaks = (double)(peak2-peak1)/(num_bins-1); // if (distance_between_peaks < THRESHOLD_GLOBAL) adaptive = true; if (distance_between_peaks < 0.5) adaptive = true; if (max1 > max2 || invert) invert = true; if (verbose) { std::cout << "Distance between light and dark: " << distance_between_peaks << std::endl; std::cout << "Max at peak 1: " << max1 << " Max at peak 2: " << max2 << std::endl; std::cout << "Color background? "<< color_background << std::endl; std::cout << "Adaptive? "<< adaptive << std::endl; std::cout << "Invert? " << invert << std::endl; } //const double kernel[]={0.0, -1.0, 0.0,-1.0, 5.0, -1.0, 0.0, -1.0, 0.0}; //image.convolve(3,kernel); if (!color_background) { image.contrast(2); image.type(GrayscaleType); } int window = std::min(image.columns(),image.rows()) / 41; if (window < 15) window = 15; if (adaptive && image.columns() > 7 && image.rows() > 7) { image.despeckle(); if (invert) { image.adaptiveThreshold(window,window,7.0); } else { image.negate(); image.adaptiveThreshold(window,window,7.0); image.negate(); } } else if (color_background) { image.despeckle(); //image = adaptive_otsu(image,window); image = Bradley_threshold(image); } if (invert) image.negate(); // image.write("tmp.png"); return(adaptive); } osra-2.1.3/src/osra_ocr.cpp0000664000175000017500000004150114115175251014252 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include // strlen(), memset() #include // isalnum(), isspace() #include // std:vector #include // std::cout #include "osra_common.h" #include "config.h" extern "C" { #include } #include #include "osra.h" #include "osra_ocr.h" #ifdef HAVE_CUNEIFORM_LIB #include #endif #ifdef HAVE_TESSERACT_LIB // We can't push these functions into this cpp file, as the types from Tesseract conflict with GOCR void osra_tesseract_init(); void osra_tesseract_destroy(); char osra_tesseract_ocr(unsigned char *pixel_map, int width, int height, const std::string &char_filter); #endif // Global GOCR variable (omg) both for 0.48-0.49 and 0.50 versions: job_t *JOB; job_t *OCR_JOB; // Also declared in osra_ocr_tesseract.cpp: const char UNKNOWN_CHAR = '_'; /** * THRESHOLD is the graylevel binarization threshold. * dropx and dropy are the coordinates for the starting point from where the connected component (the image of the character) will be searched for. * Very often there is no bounding rectangle that would exclude all extraneous pieces without cutting into the character itself. * Those pieces confuse the OCR libraries quite a bit, so it's better to extract connected components (all characters that OSRA needs to resolve * are luckily single connected components) and leave the extra bits out. */ void osra_ocr_init() { #ifdef HAVE_CUNEIFORM_LIB // Initialization for Cuneiform library should be called only once. Otherwise it breaks down during deinitialization: int langcode = PUMA_LANG_ENGLISH; PumaBool dotmatrix = 0; PumaBool fax = 0; PumaBool onecolumn = 1; PUMA_Init(0, 0); PUMA_SetImportData(PUMA_Word32_Language, &langcode); PUMA_SetImportData(PUMA_PumaBool32_DotMatrix, &dotmatrix); PUMA_SetImportData(PUMA_PumaBool32_Fax100, &fax); PUMA_SetImportData(PUMA_PumaBool32_OneColumn, &onecolumn); #endif #ifdef HAVE_TESSERACT_LIB osra_tesseract_init(); #endif } void osra_ocr_destroy() { #ifdef HAVE_CUNEIFORM_LIB PUMA_Done(); #endif #ifdef HAVE_TESSERACT_LIB osra_tesseract_destroy(); #endif } // Function: osra_gocr_ocr() // Make an attempt to OCR the image box with GOCR engine. // // Parameters: // job_t - includes pixel map and character filter // // Returns: // 0 in case the recognition failed or valid alphanumeric character char osra_gocr_ocr(job_t &gocr_job) { OCR_JOB = &gocr_job; JOB = &gocr_job; // cout<<"Before gocr"<data; if (l != NULL && strlen(l) == 1 && isalnum(l[0])) return l[0]; return UNKNOWN_CHAR; } // Function: osra_ocrad_ocr() // Make an attempt to OCR the image box with OCRAD engine. // // Parameters: // ocrad_pixmap - includes pixel map and the image mode // char_filter - character filter // // Returns: // 0 in case the recognition failed or valid alphanumeric character char osra_ocrad_ocr(const OCRAD_Pixmap * const ocrad_pixmap, const std::string &char_filter) { char result = 0; std::string line; OCRAD_Descriptor * const ocrad_res = OCRAD_open(); // If the box height is less than 10px, it should be scaled up a bit, otherwise OCRAD is unable to catch it: if (ocrad_res && OCRAD_get_errno(ocrad_res) == OCRAD_ok && OCRAD_set_image(ocrad_res, ocrad_pixmap, 0) == 0 && (ocrad_pixmap->height >= 10 || OCRAD_scale(ocrad_res, 2) == 0) && OCRAD_recognize(ocrad_res, 0) == 0) { result = OCRAD_result_first_character(ocrad_res); if (OCRAD_result_blocks(ocrad_res) >= 1 && OCRAD_result_lines(ocrad_res, 0) && OCRAD_result_line( ocrad_res, 0, 0) != 0) line = OCRAD_result_line(ocrad_res, 0, 0); } OCRAD_close(ocrad_res); // TODO: Why line should have 0 or 1 characters? Give examples... if (line.length() > 2 || !isalnum(result) || (!char_filter.empty() && char_filter.find(result, 0) == std::string::npos)) return UNKNOWN_CHAR; return result; } // Function: osra_cuneiform_ocr() // Make an attempt to OCR the image box with Cuneiform engine. // // Parameters: // cuneiform_img - pixel map // verbose - if set, then output intermediate results // char_filter - character filter // // Returns: // 0 in case the recognition failed or valid alphanumeric character #ifdef HAVE_CUNEIFORM_LIB char osra_cuneiform_ocr(Magick::Image &cuneiform_img, const std::string &char_filter) { Magick::Blob blob; cuneiform_img.write(&blob, "DIB"); size_t data_size = blob.length(); char *dib = new char[data_size]; memcpy(dib, blob.data(), data_size); char str[256]; memset(str, 0, sizeof(str)); if (!PUMA_XOpen(dib, NULL) || !PUMA_XFinalRecognition() || !PUMA_SaveToMemory(NULL, PUMA_TOTEXT, PUMA_CODE_ASCII, str, sizeof(str) - 1)) { //if (verbose) // cout << "Cuneiform recognition failed." << endl; PUMA_XClose(); delete []dib; return UNKNOWN_CHAR; } PUMA_XClose(); delete []dib; // As we have initialized the image with two identical samples, it is expected that they go in the string // one after another, or separated by space (e.g. "ZZ\n" or "Z Z\n"). if (((str[0] == str[1] && isspace(str[2])) || (str[0] == str[2] && str[1] == ' ')) && isalnum(str[0]) && (char_filter.empty() || char_filter.find(str[0], 0) != std::string::npos)) return str[0]; return UNKNOWN_CHAR; } #endif char get_atom_label(const Magick::Image &image, const Magick::ColorGray &bg, int x1, int y1, int x2, int y2, double THRESHOLD, int dropx, int dropy, bool no_filtering, bool verbose, bool numbers, const std::string &recognized_chars) { char c = UNKNOWN_CHAR; const int width = x2 - x1 + 1; const int height = y2 - y1 + 1; unsigned char *pixmap = (unsigned char *) malloc(width * height); for (int i = y1; i <= y2; i++) for (int j = x1; j <= x2; j++) pixmap[(i - y1) * width + j - x1] = (unsigned char) (255 - 255 * get_pixel(image, bg, j, i, THRESHOLD)); // Here we drop down from the top of the box, middle of x coordinate and extract connected component int t = 1; int y = dropy - y1 + 1; int x = dropx - x1; while ((t != 0) && (y < height)) { t = pixmap[y * width + x]; y++; } if (t != 0) { free(pixmap); return 0; } #pragma omp critical { y--; pixmap[y * width + x] = 2; std::list cx; std::list cy; cx.push_back(x); cy.push_back(y); while (!cx.empty()) { x = cx.front(); y = cy.front(); cx.pop_front(); cy.pop_front(); pixmap[y * width + x] = 1; // this goes around 3x3 square touching the chosen pixel for (int i = x - 1; i < x + 2; i++) for (int j = y - 1; j < y + 2; j++) if (i < width && j < height && i >= 0 && j >= 0 && pixmap[j * width + i] == 0) { cx.push_back(i); cy.push_back(j); pixmap[j * width + i] = 2; } } // Flatten the bitmap. Note: the bitmap is inverted after this cycle (255 means "empty", 0 means "pixel"). for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) pixmap[i * width + j] = (pixmap[i * width + j] == 1 ? 0 : 255); job_t gocr_job; // The list of all characters, that can be recognised as atom label: std::string char_filter = RECOGNIZED_CHARS; if (!recognized_chars.empty()) char_filter = recognized_chars; if (numbers) char_filter = "1"; if (no_filtering) char_filter.clear(); job_init(&gocr_job); job_init_image(&gocr_job); //gocr_job.cfg.cs = 160; //gocr_job.cfg.certainty = 80; //gocr_job.cfg.dust_size = 1; gocr_job.src.p.x = width; gocr_job.src.p.y = height; gocr_job.src.p.bpp = 1; gocr_job.src.p.p = pixmap; if (char_filter.empty()) gocr_job.cfg.cfilter = (char*)NULL; else gocr_job.cfg.cfilter = (char*) char_filter.c_str(); struct OCRAD_Pixmap *ocrad_pixmap = new OCRAD_Pixmap(); unsigned char *ocrad_bitmap = (unsigned char *) malloc(width * height); memset(ocrad_bitmap, 0, width * height); ocrad_pixmap->height = height; ocrad_pixmap->width = width; ocrad_pixmap->mode = OCRAD_bitmap; ocrad_pixmap->data = ocrad_bitmap; // Number of non-zero pixels on the bitmap, excluding the 1px border: int pixmap_pixels_count = 0; // Number of zero pixels on the bitmap, excluding the 1px border: int pixmap_zeros_count = 0; // The code below initialises "opix->data" buffer ("bitmap_data") for OCRAD from "tmp" buffer: #ifdef HAVE_CUNEIFORM_LIB Magick::Image cuneiform_img(Magick::Geometry(2 * width + 2, height), "white"); // From cuneiform_src/cli/cuneiform-cli.cpp::preprocess_image(Magick::Image&):168 cuneiform_img.monochrome(); cuneiform_img.type(Magick::BilevelType); #endif for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (pixmap[y * width + x] == 0) { ocrad_bitmap[y * width + x] = 1; #ifdef HAVE_CUNEIFORM_LIB // Draw two identical samples that follow one another. We do so because Cuneiform has difficulties in recognizing single characters: cuneiform_img.pixelColor(x, y, "black"); cuneiform_img.pixelColor(x + width + 2, y, "black"); #endif if (x > 0 && x < width - 1 && y > 0 && y < height - 1) pixmap_pixels_count++; } else if (x > 0 && x < width - 1 && y > 0 && y < height - 1) pixmap_zeros_count++; } } if (verbose) { std::cout << "Box to OCR: " << x1 << "x" << y1 << "-" << x2 << "x" << y2 << " w/h: " << width << "x" << height << std::endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) std::cout << (gocr_job.src.p.p[i * width + j] / 255 ? '#' : '.'); std::cout << std::endl; } } if (pixmap_pixels_count <= MIN_CHAR_POINTS || pixmap_zeros_count <= MIN_CHAR_POINTS) goto FINALIZE; c = osra_gocr_ocr(gocr_job); if (verbose) std::cout << "GOCR: c=" << c << std::endl; //c = UNKNOWN_CHAR; // Switch off GOCR recognition // Character recognition succeeded for GOCR: if (c != UNKNOWN_CHAR) goto FINALIZE; // Character recognition failed for GOCR and we try OCRAD: c = osra_ocrad_ocr(ocrad_pixmap, char_filter); if (verbose) std::cout << "OCRAD: c=" << c << std::endl; //c = UNKNOWN_CHAR; // Switch off OCRAD recognition // Character recognition succeeded for OCRAD: if (c != UNKNOWN_CHAR) goto FINALIZE; #ifdef HAVE_TESSERACT_LIB c = osra_tesseract_ocr(gocr_job.src.p.p, width, height, char_filter); if (verbose) std::cout << "Tesseract: c=" << c << std::endl; //c = UNKNOWN_CHAR; // Switch off Tesseract recognition // Character recognition succeeded for Tesseract: if (c != UNKNOWN_CHAR) goto FINALIZE; #endif #ifdef HAVE_CUNEIFORM_LIB // TODO: Why box width should be more than 7 for Cuneiform? if (width <= 7) goto FINALIZE; c = osra_cuneiform_ocr(cuneiform_img, char_filter); if (verbose) std::cout << "Cuneiform: c=" << c << std::endl; //c = UNKNOWN_CHAR; // Switch off Cuneiform recognition #endif FINALIZE: // "pixmap" is freed together with "gocr_job". job_free_image(&gocr_job); OCR_JOB = NULL; JOB = NULL; delete ocrad_pixmap; // delete OCRAD Pixmap free(ocrad_bitmap); // TODO: Why there are problems with "7" with a given box size? If the problem is engine-specific, it should be moved to appropriate section if (c == '7' && (width <= 10 || height <= 20)) c = UNKNOWN_CHAR; } // #pragma omp critical return(c == UNKNOWN_CHAR ? 0 : c); } bool detect_square_bracket(unsigned char *pic, int x, int y) { int w = -1; for (int j = x / 2; j >= 0 ; j--) { if (pic[(y / 2) * x + j] == 1) { w = j; break; } } int h1 = -1; for (int i = y / 2; i >= 0; i--) { if (pic[i * x + x / 2] == 1) { h1 = i; break; } } int h2 = y; for (int i = y / 2; i < y; i++) { if (pic[i * x + x / 2] == 1) { h2 = i; break; } } if (w > x /2 + 1 || h1 > y / 4 || (y - h2) > y / 4) return false; int total_vert(0), total_up_hor(0), total_down_hor(0), fill_vert(0), fill_up_hor(0), fill_down_hor(0), total_empty(0), fill_empty(0); for (int i = 0; i < y; i++) { for (int j = 0; j < x; j++) { bool fill = (pic[i * x + j] == 1); if (j <= w) { total_vert++; if (fill) fill_vert++; } if (i <= h1) { total_up_hor++; if (fill) fill_up_hor++; } if (i >= h2) { total_down_hor++; if (fill) fill_down_hor++; } if (j > w && i > h1 && i < h2) { total_empty++; if (fill) fill_empty++; } } } if (total_vert == 0 || total_up_hor == 0 || total_down_hor == 0 || total_empty == 0) return false; double fill_v = double(fill_vert) / total_vert; double fill_up_h = double(fill_up_hor) / total_up_hor; double fill_down_h = double(fill_down_hor) / total_down_hor; double fill_e = double(fill_empty) / total_empty; if (fill_v > 0.8 && fill_up_h > 0.7 && fill_down_h > 0.7 && fill_e < 0.2) return true; return false; } bool detect_bracket(int x, int y, unsigned char *pic) { bool res = false; #pragma omp critical { char c1 = 0; job_t job; JOB = &job; OCR_JOB = &job; job_init(&job); job_init_image(&job); job.cfg.cfilter = (char *) "([{"; //job.cfg.cs = 160; //job.cfg.certainty = 80; //job.cfg.dust_size = 1; job.src.p.x = x; job.src.p.y = y; job.src.p.bpp = 1; job.src.p.p = pic; struct OCRAD_Pixmap *ocrad_pixmap = new OCRAD_Pixmap(); unsigned char *ocrad_bitmap = (unsigned char *) malloc(x * y); memset(ocrad_bitmap, 0, x * y); ocrad_pixmap->height = y; ocrad_pixmap->width = x; ocrad_pixmap->mode = OCRAD_bitmap; ocrad_pixmap->data = ocrad_bitmap; int count = 0; int zeros = 0; for (int i = 0; i < y; i++) { for (int j = 0; j < x; j++) { if (pic[i * x + j] == 0) { ocrad_bitmap[i * x + j] = 1; count++; } else zeros++; } } /*std::string str; for (int i = 0; i < y; i++) { for (int j = 0; j < x; j++) { str += (pic[i * x + j] != 0 ? "#" : "."); } str += "\n"; } std::cout << str << std::endl; */ if (count > MIN_CHAR_POINTS && zeros > MIN_CHAR_POINTS) { try { pgm2asc(&job); } catch (...) { } char *l; l = (char *) job.res.linelist.start.next->data; if (l != NULL) c1 = l[0]; if (c1 == '(' || c1 == '[' || c1 == '{') res = true; else { char c2 = osra_ocrad_ocr(ocrad_pixmap, "([{"); if (c2 == '(' || c2 == '[' || c2 == '{') res = true; } } if (!res) res = detect_square_bracket(ocrad_bitmap, x, y); /* if (res) { std::cout << "Found! " << c1 << std::endl; } */ delete ocrad_pixmap; free(ocrad_bitmap); job_free_image(&job); OCR_JOB = NULL; JOB = NULL; } return (res); } const std::string fix_atom_name(const std::string &s, int n, const std::map &fix, const std::map &superatom, bool debug) { std::string r = s; if (s.length() == 1) r = toupper(s.at(0)); if (s == "H" && n > 1) r = "N"; std::map::const_iterator it = fix.find(s); std::string mapped = " "; if (it != fix.end()) { r = it->second; mapped = r; } if (debug && s != " " && s != "") { it = superatom.find(r); std::string smiles = " "; if (it != superatom.end()) smiles = it->second; std::cout << s << " --> " << mapped << " --> " << smiles << std::endl; } return (r); } osra-2.1.3/src/osra_ocr.h0000664000175000017500000000606214115175251013722 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_ocr.h // // Defines types and functions for OSRA OCR module. // #include // std::string #include // std::map #include // Magick::Image, Magick::ColorGray // // Section: Functions // // Function: osra_ocr_init() // // Initialises OCR engine. Should be called at e.g. program startup. // void osra_ocr_init(); // Function: osra_ocr_destroy() // // Releases all resources allocated by OCR engine. // void osra_ocr_destroy(); // Function: get_atom_label() // // OCR engine function, does single character recognition // // Parameters: // image - image object // bg - gray-level background color // x1, y1, x2, y2 - coordinates of the character box // THRESHOLD - graylevel threshold for image binarization // dropx, dropy - coordinates of drop point from where breadth-first algorithm will search for single connected component // which is hopefully the character we are trying to recognize // no_filtering - do not apply character filter // numbers - only allow numbers in the output 0..9 // recognized_chars - user-supplied ocr filter // // Returns: // recognized character or 0 char get_atom_label(const Magick::Image &image, const Magick::ColorGray &bg, int x1, int y1, int x2, int y2, double THRESHOLD, int dropx, int dropy, bool no_filtering, bool verbose, bool numbers = false, const std::string &recognized_chars=""); // Function: fix_atom_name() // // Corrects common OCR errors by using spelling dictionary // // Parameters: // s - Original atomic label as returned by OCR engine. // n - The number of bonds attached to the atom. // fix - spelling dictionary // superatom - dictionary of superatom labels mapped to SMILES // debug - enables output of debugging information to stdout // // Returns: // corrected atomic label const std::string fix_atom_name(const std::string &s, int n, const std::map &fix, const std::map &superatom, bool debug); bool detect_bracket(int x, int y, unsigned char *pic); osra-2.1.3/src/osra.h0000664000175000017500000001743114115175251013061 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra.h // // Defines types and functions exported from main module to other modules. // #ifndef OSRA_H #define OSRA_H #include // std:string #include // std::vector #include // Magick::Image, Magick::ColorGray extern "C" { #include } using namespace Magick; // struct: atom_s // Contains information about perspective atom struct atom_s { atom_s(double xx=0, double yy=0, const potrace_path_t* p=NULL) : x(xx),y(yy),min_x(xx),min_y(yy),max_x(xx),max_y(yy),curve(p),label(" "),n(0),anum(0), exists(false),corner(false),terminal(false),charge(0) {} // doubles: x, y // coordinates within the image clip double x, y; // string: label // atomic label std::string label; // int: n // counter of created OBAtom objects in int n; // int: anum // atomic number int anum; // pointer: curve // pointer to the curve found by Potrace const potrace_path_t *curve; // bools: exists, corner, terminal // atom exists, atom is at the corner (has two bonds leading to it), atom is a terminal atom bool exists, corner, terminal; // int: charge // electric charge on the atom int charge; // int: min_x, min_y, max_x, max_y // box coordinates int min_x, min_y,max_x,max_y; }; // typedef: atom_t // defines atom_t type based on atom_s struct typedef struct atom_s atom_t; // struct: bond_s // contains information about perspective bond between two atoms struct bond_s { bond_s(int i=0, int j=0, const potrace_path_t* p=NULL) : a(i),b(j),curve(p),type(1),exists(true),hash(false),wedge(false),up(false),down(false),Small(false),arom(false),conjoined(false) {} // ints: a, b, type // starting atom, ending atom, bond type (1=single, 2=double, 3=triple) int a, b, type; // pointer: curve // pointer to the curve found by Potrace const potrace_path_t *curve; // bools: exists, hash, wedge, up, down, Small, arom // bond existence and type flags bool exists; bool hash; bool wedge; bool up; bool down; bool Small; bool arom; // bool: conjoined // true for a double bond which is joined at one end on the image bool conjoined; }; // typedef: bond_t // defines bond_t type based on bond_s struct typedef struct bond_s bond_t; // Section: Constants // // Constants: global defines // // MAX_ATOMS - maximum size of the vector holding perspective atoms // MAX_FONT_HEIGHT - maximum font height at a resolution of 150 dpi // MAX_FONT_WIDTH - maximum font width at a resolution of 150 dpi // MIN_FONT_HEIGHT - minimum font height // BG_PICK_POINTS - number of points to randomly pick to determine background color // D_T_TOLERANCE - cosine tolerance to find parallel bonds for double-triple bond extraction // V_DISPLACEMENT - threshold vertical displacement in pixels // DIR_CHANGE - threshold direction change in pixels // THRESHOLD_GLOBAL - gray-level threshold for image binarization // THRESHOLD_LOW_RES - gray-level threshold for low resolutions (72 dpi) // MAX_RATIO - maximum black/white fill ratio for perspective molecular structures // MIN_ASPECT - minimum aspect ration // MAX_ASPECT - maximum aspect ratio // MIN_A_COUNT - minimum number of atoms // MAX_A_COUNT - maximum number of atoms // MIN_B_COUNT - minimum number of bonds // MAX_B_COUNT - maximum number of bonds // MIN_CHAR_POINTS - minimum number of black and white pixels in a character box // MAX_BOND_THICKNESS - maximum bond thickness // SMALL_PICTURE_AREA - threshold area of the image to be consider a small picture // NUM_RESOLUTIONS - number of resolutions to try // MAX_DASH - maximum size of a dash in a dashed bond // CC_BOND_LENGTH - average carbon-carbon bond length // FRAME - border around structure in a segmented image // SEPARATOR_ASPECT - aspect ratio for a perspective separator line // SEPARATOR_AREA - area for a perspective separator line // MAX_DIST - maximum distance in pixels between neighboring segments in image segmentation routines // MAX_AREA_RATIO - maximum area ratio for connected compoments in image segmentation // SINGLE_IMAGE_DIST - default distance between connected components in a single structure image // THRESHOLD_LEVEL - threshold level for feature matrix for image segmentation // TEXT_LINE_SIZE - maximum atomic label size in characters // PARTS_IN_MARGIN - take only every other pixel on a connected component margin for speed // BORDER_COUNT - threshold number of pixels on a box border to be considered a table // MAX_SEGMENTS - maximum number of connected compoment segments // MAX_FRAGMENTS - maximum number of fragments // STRUCTURE_COUNT - threshold number of structures to compute limits on average bond length // SPELLING_TXT - spelling file for OCR corrections // SUPERATOM_TXT - superatom file for mapping labels to SMILES #define PI 3.14159265358979323846 #define MAX_ATOMS 10000 #define MAX_FONT_HEIGHT 22 #define MAX_FONT_WIDTH 21 #define MIN_FONT_HEIGHT 5 #define BG_PICK_POINTS 1000 #define D_T_TOLERANCE 0.95 #define V_DISPLACEMENT 3 #define DIR_CHANGE 2 #define THRESHOLD_GLOBAL 0.4 #define THRESHOLD_LOW_RES 0.2 #define MAX_RATIO 0.2 #define MIN_ASPECT 0.1 #define MAX_ASPECT 10. #define MIN_A_COUNT 5 #define MAX_A_COUNT 250 #define MIN_B_COUNT 5 #define MAX_B_COUNT 250 #define MIN_CHAR_POINTS 2 #define MAX_BOND_THICKNESS 10 #define SMALL_PICTURE_AREA 6000 #define NUM_RESOLUTIONS 5 #define MAX_DASH 40 #define CC_BOND_LENGTH 1.5120 #define FRAME 5 #define SEPARATOR_ASPECT 100 #define SEPARATOR_AREA 300 #define MAX_DIST 50 #define MAX_AREA_RATIO 50 #define SINGLE_IMAGE_DIST 1000 #define MAX_DISTANCE_BETWEEN_ARROWS 210 #define THRESHOLD_LEVEL 4 #define TEXT_LINE_SIZE 8 #define PARTS_IN_MARGIN 2 #define BORDER_COUNT 3000 #define MAX_SEGMENTS 10000 #define MAX_FRAGMENTS 10 #define STRUCTURE_COUNT 20 #define SPELLING_TXT "spelling.txt" #define SUPERATOM_TXT "superatom.txt" #define RECOGNIZED_CHARS "oOcCnNHFsSBuUgMeEXYZRPp23456789AmThDGQ" #define ERROR_SPELLING_FILE_IS_MISSING -1 #define ERROR_SUPERATOM_FILE_IS_MISSING -2 #define ERROR_OUTPUT_FILE_OPEN_FAILED -3 // This error code may be returned, if ImageMagic was not able to find the .mgk files. // Check that MAGICK_CONFIGURE_PATH points to the location of *.mgk configuration files (check here http://www.imagemagick.org/script/resources.php). #define ERROR_UNKNOWN_IMAGE_TYPE -4 #define ERROR_ILLEGAL_ARGUMENT_COMBINATION -5 // This error code usually means: // (a) You have no /usr/lib/openbabel/x.x.x/smilesformat.so library installed. Install the format libraries / check http://openbabel.org/docs/dev/Installation/install.html#environment-variables // (b) The format libraries are installed, but do not correspond to /usr/lib/libopenbabel.so.y.y.y. Check they correspond to the same OpenBabel version. // (c) You need to preload OpenBabel e.g. using LD_PRELOAD=/usr/lib/libopenbabel.so #define ERROR_UNKNOWN_OPENBABEL_FORMAT -6 #endif osra-2.1.3/src/osra_openbabel.cpp0000664000175000017500000010223014115175251015413 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // std:ostringstream #include // std::cerr #include "osra_common.h" // trim() #include "osra_structure.h" #include "osra_openbabel.h" #include "osra.h" #include "osra_stl.h" #include "mcdlutil.h" using namespace OpenBabel; #define HYDROGEN_ATOMIC_NUM 1 #define LITHIUM_ATOMIC_NUM 3 #define CARBON_ATOMIC_NUM 6 #define OXYGEN_ATOMIC_NUM 8 #define FLUORINE_ATOMIC_NUM 9 #define SILICONE_ATOMIC_NUM 14 #define CHLORINE_ATOMIC_NUM 17 #define ARGON_ATOMIC_NUM 18 #define BROMINE_ATOMIC_NUM 35 #define IODINE_ATOMIC_NUM 53 #define URANIUM_ATOMIC_NUM 92 // Look at this issue: https://sourceforge.net/tracker/?func=detail&aid=3425216&group_id=40728&atid=428740 #define AROMATIC_BOND_ORDER 5 int osra_openbabel_init() { OBConversion conv; OBMol mol; bool ok = conv.SetInFormat("SMI", false); if (!ok) return ERROR_UNKNOWN_OPENBABEL_FORMAT; conv.ReadString(&mol, "[*]"); if (mol.NumAtoms() == 0) return ERROR_UNKNOWN_OPENBABEL_FORMAT; return 0; } // Function: create_atom() // // For the atom represented by its OCR'ed label create a new atom in the given molecule. // // Parameters: // mol - the current molecule // atom - atom to add to molecule (will be updated) // scale - scale factor / coordinates multiplier // superatom - superatom dictionary, that maps atom labels to corresponding SMILES // verbose - print debug information // // Returns: // true in case the given atom is superatom // bool create_atom(OBMol &mol, atom_t &atom, double scale, const std::map &superatom, bool verbose) { if (atom.label.empty() || atom.label == " ") { atom.anum = CARBON_ATOMIC_NUM; } else { // Lookup in superatom dictionary: std::map::const_iterator it = superatom.find(atom.label); if (it != superatom.end()) { // "superatom" case (e.g. "COOH") const std::string &smiles_superatom = it->second; OBConversion conv; OBMol superatom_mol; conv.SetInFormat("SMI"); conv.ReadString(&superatom_mol, smiles_superatom); if (verbose) std::cout << "Considering superatom " << atom.label << "->" << smiles_superatom << " vector: " << atom.x * scale << "x" << -atom.y * scale << '.' << std::endl; // This is the index of first atom in superatom in molecule: atom.n = mol.NumAtoms() + 1; OBAtomIterator atom_iter; // Transfer all atoms from "superatom" molecule to current molecule. for (OBAtom *a = superatom_mol.BeginAtom(atom_iter); a; a = superatom_mol.NextAtom(atom_iter)) { if (verbose) std::cout << "Adding atom #" << mol.NumAtoms() + 1 << ", anum: " << a->GetAtomicNum() << std::endl; OBAtom *new_atom = mol.NewAtom(); new_atom->SetAtomicNum(a->GetAtomicNum()); new_atom->SetFormalCharge(a->GetFormalCharge()); if (!atom.label.empty() && atom.label != " ") { // Unknown atom? OBPairData *label = new OBPairData; label->SetAttribute("UserLabel"); label->SetValue(atom.label); label->SetOrigin(userInput); // set by user, not by Open Babel new_atom->SetData(label); } if (atom.anum == 0) { AliasData* ad = new AliasData(); ad->SetAlias(atom.label); ad->SetOrigin(external); new_atom->SetData(ad); } } // Correct first atom meta-info: OBAtom *first_superatom = mol.GetAtom(atom.n); first_superatom->SetVector(atom.x * scale, -atom.y * scale, 0); atom.anum = first_superatom->GetAtomicNum(); int first_bond_index = mol.NumBonds(); OBBondIterator bond_iter; // Transfer all bonds from "superatom" molecule to current molecule: for (OBBond *b = superatom_mol.BeginBond(bond_iter); b; b = superatom_mol.NextBond(bond_iter)) { if (verbose) std::cout << "Adding bond #" << mol.NumBonds() << " " << b->GetBeginAtomIdx() + atom.n - 1 << "->" << b->GetEndAtomIdx() + atom.n - 1 << ", order: " << b->GetBondOrder() << ", flags: " << b->GetFlags() << '.' << std::endl; mol.AddBond(b->GetBeginAtomIdx() + atom.n - 1, b->GetEndAtomIdx() + atom.n - 1, b->GetBondOrder(), b->GetFlags()); } // If at least one bond was added, the "superatom" coordinates should be recalculated: return first_bond_index != mol.NumBonds(); } // If not found, lookup the atom number in periodic table of elements: atom.anum = OBElements::GetAtomicNum(atom.label.c_str()); } atom.n = mol.NumAtoms() + 1; if (verbose) std::cout << "Creating atom #" << atom.n << " \"" << atom.label << "\", anum: " << atom.anum << '.' << std::endl; OBAtom *a = mol.NewAtom(); a->SetAtomicNum(atom.anum); a->SetVector(atom.x * scale, -atom.y * scale, 0); if (atom.charge != 0) a->SetFormalCharge(atom.charge); if (atom.anum != 0 && !atom.label.empty() && atom.label != " ") { // Unknown atom? OBPairData *label = new OBPairData; label->SetAttribute("UserLabel"); label->SetValue(atom.label); label->SetOrigin(userInput); // set by user, not by Open Babel a->SetData(label); } if (atom.anum == 0) { AliasData* ad = new AliasData(); ad->SetAlias(atom.label); ad->SetOrigin(external); a->SetData(ad); } return false; } // Function: confidence_function() // // Calculates confidence estimate based on molecular counts provided by // // Parameters: // C_Count, N_Count, O_Count, F_Count, S_Count, Cl_Count, Br_Count - number of carbon, nitrogen, oxygen, fluorine, sulfur, chlorine, and bromine atoms // R_Count - number of recognized Markush atomic labels, such as R1, R2.... // Xx_Count - number of unrecognized atomic labels from // num_rings - number of rings // num_aromatic - number of aromatic rings // num_fragments - number of fragments // Num_Rings - vector of counts for number of 3,4,5,6,7-member rings // // Returns: // confidence estimate double confidence_function(unsigned int* x, unsigned int n) { double c[] = {-0.11469143725730054, 0.15723547931889853, 0.19765680222250673, 0.249101590474403, 0.1897669087341134, 0.19588348907301223, 0.3354622208036507, 0.16779269801176255, -0.21232000222198893, 0.016958281784354032, -0.08672059360133752, -0.05105752296619957, -0.349912750824004, 0.18836317536530647, 0.22316782354758827, 0.27741998968081166, 0.25710999274481955, 0.27968899280120096, 0.12695166847876285, -0.10020778884718293, 0.05150631410596443, 0.22283571763712148, 0.23130179826714167, 0.1049054095759948, 0.05333970810460394, -0.12491056666737535}; double r = 0; for (unsigned int i=0; iGetParent()); OBTetrahedralStereo *stereo; if (facade.HasTetrahedralStereo(atom->GetId())) stereo = facade.GetTetrahedralStereo(atom->GetId()); else stereo = new OBTetrahedralStereo(atom->GetParent()); OBTetrahedralStereo::Config config = stereo->GetConfig(); config.center = atom->GetId(); config.specified = true; config.winding = OBStereo::UnknownWinding; //config.specified = false; stereo->SetConfig(config); if (!facade.HasTetrahedralStereo(atom->GetId())) atom->GetParent()->SetData(stereo); } // Function: create_molecule() // // Converts vectors of atoms and bonds into a molecular object and calculates the molecule statistics. // Note: this function changes the atoms! // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - total number of bonds // avg_bond_length - average bond length as measured from the image (to be included into output if provided) // molecule_statistics - the molecule statistics (returned to the caller) // generate_2D_coordinates - generate 2D coordinates for chemical groups // confidence - confidence score (returned to the caller if provided) // superatom - dictionary of superatom labels mapped to SMILES // verbose - print debug info void create_molecule(OBMol &mol, std::vector &atom, const std::vector &bond, int n_bond, double avg_bond_length, molecule_statistics_t &molecule_statistics, bool generate_2D_coordinates, double * const confidence, const std::map &superatom, int n_letters, std::string * const confidence_parameters, bool verbose, const std::vector & brackets) { std::string str; double scale = CC_BOND_LENGTH / avg_bond_length; // The indexes of "superatoms" and the bonds, that connect these superatoms to the molecule: std::vector super_atoms, super_bonds; int anum; bool needs_kekulization = false; mol.SetDimension(2); mol.BeginModify(); for (int i = 0; i < n_bond; i++) if (bond[i].exists && i < MAX_ATOMS - 1 && bond[i].a < MAX_ATOMS - 1 && bond[i].b < MAX_ATOMS - 1) { atom_t* bond_atoms[] = { &atom[bond[i].a], &atom[bond[i].b] }; for (int j = 0; j < 2; j++) if (bond_atoms[j]->n == 0) { if (create_atom(mol, *bond_atoms[j], scale, superatom, verbose)) { super_atoms.push_back(bond_atoms[j]->n); // The current bond (next to be added) connects the super atom (bond_atoms[j]) with the molecule: super_bonds.push_back(mol.NumBonds()); } } if (bond[i].hash && !bond[i].wedge) { if (verbose) std::cout << "Creating hash bond #" << mol.NumBonds() << " " << atom[bond[i].a].n << "<->" << atom[bond[i].b].n << ", order: " << bond[i].type << ", flags: " << OB_HASH_BOND << '.' << std::endl; if (atom[bond[i].a].anum == OXYGEN_ATOMIC_NUM || atom[bond[i].a].anum == HYDROGEN_ATOMIC_NUM || atom[bond[i].a].anum == FLUORINE_ATOMIC_NUM || atom[bond[i].a].anum == IODINE_ATOMIC_NUM || atom[bond[i].a].anum == CHLORINE_ATOMIC_NUM || atom[bond[i].a].anum == BROMINE_ATOMIC_NUM || atom[bond[i].a].anum == ARGON_ATOMIC_NUM || atom[bond[i].a].terminal) mol.AddBond(atom[bond[i].b].n, atom[bond[i].a].n, bond[i].type, OB_HASH_BOND); else mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, bond[i].type, OB_HASH_BOND); } else if (!bond[i].hash && bond[i].wedge) { if (atom[bond[i].a].anum == OXYGEN_ATOMIC_NUM || atom[bond[i].a].anum == HYDROGEN_ATOMIC_NUM || atom[bond[i].a].anum == FLUORINE_ATOMIC_NUM || atom[bond[i].a].anum == IODINE_ATOMIC_NUM || atom[bond[i].a].anum == CHLORINE_ATOMIC_NUM || atom[bond[i].a].anum == BROMINE_ATOMIC_NUM || atom[bond[i].a].anum == ARGON_ATOMIC_NUM || atom[bond[i].a].terminal) mol.AddBond(atom[bond[i].b].n, atom[bond[i].a].n, bond[i].type, OB_WEDGE_BOND); else mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, bond[i].type, OB_WEDGE_BOND); } else if (bond[i].arom) { if (verbose) std::cout << "Creating aromatic bond #" << mol.NumBonds() << " " << atom[bond[i].a].n << "->" << atom[bond[i].b].n << ", order: " << AROMATIC_BOND_ORDER << '.' << std::endl; //mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, AROMATIC_BOND_ORDER); mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, 1, OBBond::Aromatic); needs_kekulization = true; } else { int bond_flags = 0; /* if (bond[i].up) bond_flags = OB_TORUP_BOND; else if (bond[i].down) bond_flags = OB_TORDOWN_BOND; */ if (verbose) std::cout << "Creating bond #" << mol.NumBonds() << " " << atom[bond[i].a].n << "->" << atom[bond[i].b].n << ", type: " << bond[i].type << ", flags: " << bond_flags << '.' << std::endl; if (bond[i].wedge && bond[i].hash) // wavy bonds { mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, bond[i].type, OB_WEDGE_OR_HASH_BOND); OBMolAtomIter a,b; FOR_ATOMS_OF_MOL(ai, mol) if (ai->GetIdx() == atom[bond[i].b].n) b = ai; else if (ai->GetIdx() == atom[bond[i].a].n) a = ai; SetTetrahedtalUnknown(a); SetTetrahedtalUnknown(b); } else mol.AddBond(atom[bond[i].a].n, atom[bond[i].b].n, bond[i].type, bond_flags); } } if (needs_kekulization) { mol.SetAromaticPerceived(); // First of all, set the atoms at the ends of the aromatic bonds to also // be aromatic. This information is required for OBKekulize. FOR_BONDS_OF_MOL(bond, mol) { if (bond->IsAromatic()) { bond->GetBeginAtom()->SetAromatic(); bond->GetEndAtom()->SetAromatic(); } } bool ok = OpenBabel::OBKekulize(&mol); mol.SetAromaticPerceived(false); } mol.EndModify(); mol.FindRingAtomsAndBonds(); // Clear the counters of created OBAtom objects: for (int i = 0; i < n_bond; i++) if (bond[i].exists) { atom[bond[i].a].n = 0; atom[bond[i].b].n = 0; } // The logic below calculates the information both for molecule statistics and for confidence function: OBBondIterator bond_iter; unsigned int Num_HashBonds = 0; unsigned int Num_WedgeBonds = 0; unsigned int Num_DoubleBonds = 0; // This block modifies the molecule: for (OBBond *b = mol.BeginBond(bond_iter); b; b = mol.NextBond(bond_iter)) { if (b->IsInRing()) { // Clear any indication of "/" and "\" double bond stereochemistry: //b->UnsetUp(); //b->UnsetDown(); } else // Clear all aromaticity information for the bond (affects "num_aromatic" variable below): b->SetAromatic(false); if (b->IsHash()) Num_HashBonds++; if (b->IsWedge()) Num_WedgeBonds++; if (b->GetBondOrder() == 2 && (b->GetBeginAtom()->GetAtomicNum() == OBElements::Oxygen || b->GetEndAtom()->GetAtomicNum() == OBElements::Oxygen)) Num_DoubleBonds++; } std::vector Num_Rings(8, 0); // number of rings of the given size (e.g. "Num_Rings[2]" = number of rings of size 2) unsigned int num_rings = 0; // total number of rings unsigned int num_aromatic = 0; // total number of aromatic rings { OBMol mol_without_R(mol); mol_without_R.BeginModify(); OBAtomIterator atom_iter_R; for (OBAtom *a = mol_without_R.BeginAtom(atom_iter_R); a; a = mol_without_R.NextAtom(atom_iter_R)) if (a->GetAtomicNum() == 0) a->SetAtomicNum(CARBON_ATOMIC_NUM); mol_without_R.EndModify(); mol_without_R.FindRingAtomsAndBonds(); // Get the Smallest Set of Smallest Rings: std::vector vr = mol_without_R.GetSSSR(); for (std::vector::iterator iter = vr.begin(); iter != vr.end(); iter++) { num_rings++; if ((*iter)->IsAromatic()) num_aromatic++; if ((*iter)->Size() < 8) Num_Rings[(*iter)->Size()]++; } } // Get a list of contiguous fragments sorted by size from largest to smallest: std::vector > cfl; mol.ContigFragList(cfl); molecule_statistics.rotors = mol.NumRotors(); molecule_statistics.fragments = cfl.size(); molecule_statistics.rings456 = Num_Rings[5] + Num_Rings[6] + Num_Rings[4]; molecule_statistics.rings56 = Num_Rings[5] + Num_Rings[6]; molecule_statistics.num_atoms = mol.NumAtoms(); molecule_statistics.num_bonds = mol.NumBonds(); if (confidence) { unsigned int C_Count = 0; unsigned int N_Count = 0; unsigned int O_Count = 0; unsigned int F_Count = 0; unsigned int S_Count = 0; unsigned int Cl_Count = 0; unsigned int Br_Count = 0; unsigned int R_Count = 0; unsigned int Xx_Count = 0; unsigned int Si_Count = 0; unsigned int Me_Count = 0; unsigned int U_Count = 0; unsigned int Li_Count = 0; unsigned int PositiveCharge = 0; OBAtomIterator atom_iter; for (OBAtom *a = mol.BeginAtom(atom_iter); a; a = mol.NextAtom(atom_iter)) { if (a->GetAtomicNum() == OBElements::Carbon) { C_Count++; AliasData *ad = (AliasData *) a->GetData("UserLabel"); if (ad != NULL && ad->GetAlias() != " " && !ad->GetAlias().empty()) { C_Count--; Me_Count++; } } else if (a->GetAtomicNum() == OBElements::Nitrogen) N_Count++; else if (a->GetAtomicNum() == OBElements::Oxygen) O_Count++; else if (a->GetAtomicNum() == OBElements::Sulfur) S_Count++; else if (a->GetAtomicNum() == FLUORINE_ATOMIC_NUM) F_Count++; else if (a->GetAtomicNum() == CHLORINE_ATOMIC_NUM) Cl_Count++; else if (a->GetAtomicNum() == BROMINE_ATOMIC_NUM) Br_Count++; else if (a->GetAtomicNum() == SILICONE_ATOMIC_NUM) Si_Count++; else if (a->GetAtomicNum() == URANIUM_ATOMIC_NUM) U_Count++; else if (a->GetAtomicNum() == LITHIUM_ATOMIC_NUM) Li_Count++; else if (a->GetAtomicNum() == 0) { AliasData *ad = (AliasData *) a->GetData("UserLabel"); if (ad != NULL && ad->GetAlias() != "Xx" && ad->GetAlias() != "*") R_Count++; else Xx_Count++; } else { AliasData *ad = (AliasData *) a->GetData("UserLabel"); if (ad != NULL && ad->GetAlias() != "Xx" && ad->GetAlias() != " " && ad->GetAlias() != "*" && !ad->GetAlias().empty()) R_Count++; } if (a->GetFormalCharge() > 0) PositiveCharge++; } molecule_statistics.num_organic_non_carbon_atoms = N_Count + F_Count + Cl_Count + Br_Count; int num_angles = 0; //vector angles; if (super_atoms.empty()) { OBBondIterator bond_iter1; for (OBBond *b1 = mol.BeginBond(bond_iter1); b1; b1 = mol.NextBond(bond_iter1)) { unsigned int a = b1->GetBeginAtomIdx(); unsigned int b = b1->GetEndAtomIdx(); if ( b1->GetBeginAtom()->GetAtomicNum() == OBElements::Hydrogen || b1->GetEndAtom()->GetAtomicNum() == OBElements::Hydrogen || b1->GetLength() == 0) continue; OBBondIterator bond_iter2; for (OBBond *b2 = mol.BeginBond(bond_iter2); b2; b2 = mol.NextBond(bond_iter2)) { if (b1->GetIdx() < b2->GetIdx()) { unsigned int c = b2->GetBeginAtomIdx(); unsigned int d = b2->GetEndAtomIdx(); if ( b2->GetBeginAtom()->GetAtomicNum() == OBElements::Hydrogen || b2->GetEndAtom()->GetAtomicNum() == OBElements::Hydrogen || b2->GetLength() == 0) continue; double angle; bool found = false; if (a == c && b != d) { angle = mol.GetAngle(b1->GetEndAtom(), b1->GetBeginAtom(), b2->GetEndAtom()); found = true; } else if (a == d && b != c) { angle = mol.GetAngle(b1->GetEndAtom(), b1->GetBeginAtom(), b2->GetBeginAtom()); found = true; } else if (b == c && a != d) { angle = mol.GetAngle(b1->GetBeginAtom(), b1->GetEndAtom(), b2->GetEndAtom()); found = true; } else if (b == d && a != c) { angle = mol.GetAngle(b1->GetBeginAtom(), b1->GetEndAtom(), b2->GetBeginAtom()); found = true; } if (found ) { //angles.push_back(angle); if (angle < 20) num_angles++; } } } } } molecule_statistics.num_small_angles = num_angles; /*if (!angles.empty()) sort(angles.begin(),angles.end()); for (size_t kk = 0; kk < angles.size(); kk++) cout << angles[kk] << endl; cout << "===" << endl; */ //cout<(n_letters),mol.NumAtoms(),mol.NumHvyAtoms(),mol.NumBonds()}; unsigned int n = sizeof(x)/sizeof(unsigned int); *confidence = confidence_function(x,n); if (confidence_parameters) { std::stringstream cpss; for (int i = 0; i < n-1; i++) cpss << x[i]<<","; cpss << x[n-1]; // 0 1 2 3 4 5 6 7 8 9 10 11 // cpss<SetAttribute("_SGroupBrackets"); group->SetOrigin(userInput); for (size_t i = 0; i < brackets.size(); i++) { double x1 = double(brackets[i].box.x1) * scale; double y1 = -double(brackets[i].box.y1) * scale; double x2 = double(brackets[i].box.x2) * scale; double y2 = -double(brackets[i].box.y2) * scale; std::set visited; std::set nbrs; OBAtom* start(NULL); OBAtom* finish(NULL); int bond1 = -1; int bond2 = -1; OBBondIterator bond_iter; for (OBBond *b = mol.BeginBond(bond_iter); b; b = mol.NextBond(bond_iter)) { OBAtom *a1 = b->GetBeginAtom(); OBAtom *a2 = b->GetEndAtom(); if (a1->y() > y2 && a1->y() < y1 && a2->y() > y2 && a2->y() < y1) { if (a1->x() < x1 && a2->x() > x1) { start = a1; visited.insert(a1); nbrs.insert(a2); bond1 = b->GetIdx(); } if (a1->x() > x1 && a2->x() < x1) { start = a2; visited.insert(a2); nbrs.insert(a1); bond1 = b->GetIdx(); } if (a1->x() < x2 && a2->x() > x2) { finish = a2; visited.insert(a2); nbrs.insert(a1); bond2 = b->GetIdx(); } if (a1->x() > x2 && a2->x() < x2) { finish = a1; visited.insert(a1); nbrs.insert(a2); bond2 = b->GetIdx(); } } } if (bond1 < 0 || bond2 < 0 || bond1 == bond2) continue; OBBond *b1 = mol.GetBond(bond1); OBBond *b2 = mol.GetBond(bond2); OBPairData *i1 = new OBPairData; i1->SetAttribute("_SGroup"); i1->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%d", group_id); i1->SetValue(buff); b1->SetData(i1); OBPairData *i2 = new OBPairData; i2->SetAttribute("_SGroup"); i2->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%d", group_id); i2->SetValue(buff); b2->SetData(i2); while (!nbrs.empty()) { OBAtom *a = *nbrs.begin(); OBBondIterator bond_iter2; for (OBAtom *b = a->BeginNbrAtom(bond_iter2); b; b = a->NextNbrAtom(bond_iter2)) { if (visited.find(b) == visited.end()) nbrs.insert(b); } visited.insert(a); nbrs.erase(a); } visited.erase(start); visited.erase(finish); for (std::set::iterator it = visited.begin(); it != visited.end(); ++it) { OBAtom *a = *it; OBPairData *i3 = new OBPairData; i3->SetAttribute("_SGroup"); i3->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%d", group_id); i3->SetValue(buff); a->SetData(i3); } OBSetData *coords = new OBSetData; coords->SetAttribute("Coordinates"); coords->SetOrigin(userInput); OBPairData *dx1 = new OBPairData; dx1->SetAttribute("x1"); dx1->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%10.4f", x1); dx1->SetValue(buff); coords->AddData(dx1); OBPairData *dy1 = new OBPairData; dy1->SetAttribute("y1"); dy1->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%10.4f", y1); dy1->SetValue(buff); coords->AddData(dy1); OBPairData *dx2 = new OBPairData; dx2->SetAttribute("x2"); dx2->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%10.4f", x2); dx2->SetValue(buff); coords->AddData(dx2); OBPairData *dy2 = new OBPairData; dy2->SetAttribute("y2"); dy2->SetOrigin(userInput); snprintf(buff, BUFF_SIZE, "%10.4f", y2); dy2->SetValue(buff); coords->AddData(dy2); OBPairData *lbl = new OBPairData; lbl->SetAttribute("label"); lbl->SetOrigin(userInput); lbl->SetValue(brackets[i].a); coords->AddData(lbl); group->AddData(coords); group_id++; } mol.SetData(group); } } } molecule_statistics_t calculate_molecule_statistics( std::vector &atom, const std::vector &bond, int n_bond, double avg_bond_length, const std::map &superatom, bool verbose) { molecule_statistics_t molecule_statistics; #pragma omp critical { OBMol mol; std::vector brackets; create_molecule(mol, atom, bond, n_bond, avg_bond_length, molecule_statistics, false, NULL, superatom, 0, NULL, false, brackets); mol.Clear(); } if (verbose) std::cout << "Molecule fragments: " << molecule_statistics.fragments << '.' << std::endl; return molecule_statistics; } const std::string get_formatted_structure( std::vector &atom, const std::vector &bond, int n_bond, const std::string &format, const std::string &embedded_format, molecule_statistics_t &molecule_statistics, double &confidence, bool show_confidence, double avg_bond_length, double scaled_avg_bond_length, bool show_avg_bond_length, const int * const resolution, const int * const page, const box_t * const surrounding_box, const std::map &superatom, int n_letters, bool show_learning, int resolution_iteration, bool verbose, const std::vector& brackets) { std::ostringstream strstr; #pragma omp critical { OBMol mol; std::string confidence_parameters; create_molecule(mol, atom, bond, n_bond, avg_bond_length, molecule_statistics, format == "sdf" || format == "mol" || format == "sd" || format == "mdl", &confidence, superatom, n_letters, &confidence_parameters, verbose, brackets); FOR_ATOMS_OF_MOL(matom, mol) { OBAtomAssignTypicalImplicitHydrogens(&*matom); } mol.SetHydrogensAdded(false); // Add hydrogens to the entire molecule to fill out implicit valence spots: bool ok = mol.AddHydrogens(true, false); mol.DeleteData(OBGenericDataType::StereoData); // Add single bonds based on atom proximity: mol.ConnectTheDots(); // Copies each disconnected fragment as a separate OBMol: //mol.Separate(); // Deletes all atoms except for the largest contiguous fragment: mol.StripSalts(MIN_A_COUNT); if (show_confidence) { OBPairData *label = new OBPairData; label->SetAttribute("Confidence_estimate"); std::ostringstream cs; cs << confidence; label->SetValue(cs.str()); mol.SetData(label); } if (show_learning) { OBPairData *label = new OBPairData; label->SetAttribute("Confidence_parameters"); std::ostringstream cs; cs << confidence_parameters; label->SetValue(cs.str()); mol.SetData(label); OBPairData *label1 = new OBPairData; label1->SetAttribute("Resolution_iteration"); std::ostringstream cs1; cs1 << resolution_iteration; label1->SetValue(cs1.str()); mol.SetData(label1); } if (show_avg_bond_length) { OBPairData *label = new OBPairData; label->SetAttribute("Average_bond_length"); std::ostringstream cs; cs << scaled_avg_bond_length; label->SetValue(cs.str()); mol.SetData(label); } if (resolution) { OBPairData *label = new OBPairData; label->SetAttribute("Resolution"); std::ostringstream cs; cs << *resolution; label->SetValue(cs.str()); mol.SetData(label); } if (page) { OBPairData *label = new OBPairData; label->SetAttribute("Page"); std::ostringstream cs; cs << *page; label->SetValue(cs.str()); mol.SetData(label); } if (surrounding_box) { OBPairData *label = new OBPairData; label->SetAttribute("Surrounding_box"); std::ostringstream cs; cs << surrounding_box->x1 << 'x' << surrounding_box->y1 << '-' << surrounding_box->x2 << 'x' << surrounding_box->y2; label->SetValue(cs.str()); mol.SetData(label); } if (!embedded_format.empty()) { std::string value; OBConversion conv; conv.SetOutFormat(embedded_format.c_str()); value = conv.WriteString(&mol); trim(value); if (!value.empty()) { OBPairData *label = new OBPairData; label->SetAttribute(embedded_format); label->SetValue(value.c_str()); mol.SetData(label); if (embedded_format == "inchi") { conv.SetOptions("K", conv.OUTOPTIONS); value = conv.WriteString(&mol); trim(value); label = new OBPairData; label->SetAttribute("InChI_key"); label->SetValue(value.c_str()); mol.SetData(label); } } } OBConversion conv; conv.AddOption("w", conv.OUTOPTIONS); conv.SetOutFormat(format.c_str()); strstr << conv.WriteString(&mol, true); if (format == "smi" || format == "can") { if (show_avg_bond_length) strstr << " " << scaled_avg_bond_length; if (resolution) strstr << " " << *resolution; if (show_confidence) strstr << " " << confidence; if (show_learning) strstr << " " << confidence_parameters<<" "<x1 << 'x' << surrounding_box->y1 << '-' << surrounding_box->x2 << 'x' << surrounding_box->y2; } strstr << std::endl; mol.Clear(); } if (verbose) std::cout << "Structure length: " << strstr.str().length() << ", molecule fragments: " << molecule_statistics.fragments << '.' << std::endl; return (strstr.str()); } osra-2.1.3/src/osra_anisotropic.h0000664000175000017500000000447314115175251015475 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_anisotropic.h // // Defines types and functions for anisotropic smoothing module. // #include // Magick::Image // // Section: Functions // // Function: anisotropic_smoothing() // // Performs Greycstoration anisotropic smoothing on an image according to the specified parameters // // Parameters: // image - image object // width - width of image // height - height of image // amplitude - amplitude of smoothing // sharpness - sharpness parameter // anisotropy - anisotropy parameter // alpha - alpha parameter for smoothing // sigma - sigma parameter for smoothing' // // Returns: // image object // // See also: // Magick::Image anisotropic_smoothing(const Magick::Image &image, int width, int height, const float amplitude, const float sharpness, const float anisotropy, const float alpha, const float sigma); // Function: anisotropic_scaling() // // Performs Greycstoration anisotropic scaling on an image // // Parameters: // image - image object // width - width of image // height - height of image // nw - new width // nh - new height // // Returns: // image object // // See also: // Magick::Image anisotropic_scaling(const Magick::Image &image, int width, int height, int nw, int nh); osra-2.1.3/src/osra_segment.h0000664000175000017500000001411714115175251014601 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_segment.h // // Declares page segmentation functions // #ifndef OSRA_SEGMENT_H #define OSRA_SEGMENT_H #include // sdt::list #include // std::vector #include #include // fabs(double) #include // FLT_MAX #include // INT_MAX #include // std::min(double, double), std::max(double, double) #include using namespace Magick; // struct: point_s // a point of the image, used by image segmentation routines struct point_s { // int: x,y // coordinates of the image point int x, y; explicit point_s(int a, int b) : x(a), y(b) {} point_s() {} }; // typedef: point_t // defines point_t type based on point_s struct typedef struct point_s point_t; // struct: box_s // encompassing box structure for image segmentation struct box_s { // int: x1, y1, x2, y2 // coordinates of top-left and bottom-right corners int x1, y1, x2, y2; // array: c // vector of points in the box std::vector c; }; // typedef: box_t // defines box_t type based on box_s struct typedef struct box_s box_t; // struct: arrow_s // coordinates of tail and head of an arrow struct arrow_s { arrow_s(point_t _head, point_t _tail,int _min_x,int _min_y,int _max_x,int _max_y) : head(_head),tail(_tail),min_x(_min_x),min_y(_min_y),max_x(_max_x),max_y(_max_y),linebreak(false),linebreak_before(false),reversible(false),remove(false),agent("") {} arrow_s() {} // point_t: tail, head // tail and head of an arrow as points point_t tail,head; int min_x,min_y,max_x,max_y; std::string agent; bool linebreak; bool linebreak_before; bool reversible; bool remove; }; // typedef: arrow_t // defines arrow_t type based on arrow_s struct typedef struct arrow_s arrow_t; struct plus_s { point_t center; int min_x,min_y,max_x,max_y; }; typedef struct plus_s plus_t; // // Section: Functions // // Function: find_segments() // // Performs page segmentation to different regions (text/graphics/linear etc.) // // Parameters: // image - page image // threshold - black-white binarization threshold // bgColor - background color // adaptive - flag set if adaptive thresholding has been used in grayscale conversion // is_reaction - flag set if we're looking for reaction-specific symbols (arrows, plus signs etc.) // arrows - a vector of arrows found during segmentation // pluses - a vector of plus centers found during segmentation // verbose - flag set for verbose reporting // // Returns: // A list of clusters, each of which is a list of connected segments each of which is a list of points void find_segments(const Image &image, double threshold, const ColorGray &bgColor, bool adaptive, bool is_reaction, std::vector &arrows, std::vector &pluses, bool keep, bool verbose, std::list > > &explicit_clusters); // Function: prune_clusters() // // Prunes the list of clusters and retains only molecular structure images // // Parameters: // clusters - a list of clusters detected by // boxes - a vector of objects for molecular structure images // brackets - a vector of points which potentially belong to brackets // // Returns: // Number of molecular structure images int prune_clusters(std::list > > &clusters, std::vector &boxes, std::set > &brackets); template void build_hist(const T &seg, std::vector &hist, const int len, int &top_pos, int &top_value,point_t &head,point_t &tail, point_t ¢er, int &min_x, int &min_y, int &max_x, int &max_y) { int l=seg.size(); typename T::const_iterator j; center.x=0; center.y=0; min_x = INT_MAX; min_y = INT_MAX; max_x = 0; max_y = 0; for (j=seg.begin(); j!=seg.end(); j++) { center.x += j->x; center.y += j->y; min_x = std::min(min_x, j->x); min_y = std::min(min_y, j->y); max_x = std::max(max_x, j->x); max_y = std::max(max_y, j->y); } center.x /=l; // Find the center of mass for the segment margin center.y /=l; for (j=seg.begin(); j!=seg.end(); j++) { int dx = j->x-center.x; int dy = j->y-center.y; double r=(double)sqrt(dx*dx+dy*dy); double theta=0.; if (dx!=0 || dy!=0) theta = atan2(dy,dx); int bin = (theta+M_PI)*len/(2*M_PI); if (bin>=len) bin -= len; hist[bin]++; // build a histogram of occurencies in polar coordinates if (hist[bin]>=top_value) { top_pos = bin; // find the position of the highest peak top_value = hist[bin]; } } double r_max=0; for (j=seg.begin(); j!=seg.end(); j++) { int dx = j->x-center.x; int dy = j->y-center.y; double r=(double)sqrt(dx*dx+dy*dy); double theta=0.; if (dx!=0 || dy!=0) theta = atan2(dy,dx); int bin = (theta+M_PI)*len/(2*M_PI); if (bin>=len) bin -= len; if (bin == top_pos && r>r_max) { head = *j; r_max = r; } } r_max=0; for (j=seg.begin(); j!=seg.end(); j++) { int dx = j->x-head.x; int dy = j->y-head.y; double r=(double)sqrt(dx*dx+dy*dy); if (r>r_max) { r_max = r; tail = *j; } } } #endif osra-2.1.3/src/mcdlutil.h0000664000175000017500000001323214115175251013725 0ustar igorigor/*-*-C++-*- ********************************************************************** Copyright (C) 2007,2008 by Sergei V. Trepalin sergey_trepalin@chemical-block.com Copyright (C) 2007,2008 by Andrei Gakh andrei.gakh@nnsa.doe.gov This file is part of the Open Babel project. For more information, see 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 2 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 GNU General Public License for more details. *********************************************************************** */ /* Diagram is generated using templates, which are stored in SD file templates.sdf The SD file is usual SD file, which contain chemical structures and might contain data. Only chemical structures are used. Subgraph isomorphisme search is executed and coordinates of atoms are determined from templates. See Molecules, 11, 129-141 (2006) for algorithm decription. Structures in SD file are converted in next manner: 1. All atoms, except explicit hydrogens, are replaced with generic ANY_ATOM (matched with any atom in subgraph isomorphisme search) 2. All bonds are replaces with generic ANY_BOND, which can be matched with any bond in molecule 3. All hydrogen are removed, but they are used for search-query and structure atom matching is believed fo be sucessfukk if chemical structure contains more or equal number of hydrogens, than query. Using explicitly-defined hydrogens on query enables ones to remove substitutors attachment for atom, which are sterically hidden on templates if the file will not be found, predefined templates will be used */ namespace OpenBabel { //common constants static const int MAXBONDS=300; static const int MAXFRAGS=200; static const int MAXCHARS=1000; static const int MAX_DEPTH=10; static const int NELEMMAX=120; #define NELEMMCDL 121 // Return valency by hydrogen for given atomic position in the Periodic Table int hydrogenValency(int na); int maxValency(int na); //Alternate overloaded methods int alternate(OBMol * pmol, const int nH[], int bondOrders []); //This method does not work! //Zero-based atomic numeration should be in connection matrix arrays iA1 and iA2-so first atom has indez zero int alternate(const std::vector aPosition,const std::vector aCharge, const std::vector aRad,const std::vector nHydr, const std::vector iA1, const std::vector iA2, std::vector & bondOrders, int nAtoms, int nBonds); //Diagram generation overloaded methods void generateDiagram(OBMol * pmol); //Zero-based atomic numeration should be in connection matrix arrays iA1 and iA2-so first atom has indez zero void generateDiagram(const std::vector iA1, const std::vector iA2, std::vector& rx, std::vector& ry, int nAtoms, int nBonds); void generateDiagram(OBMol * pmol, std::ostream & ofs); //for testing purposes only //Fragment search - pure subgraph isomorphisme bool fragmentSearch(OBMol * query, OBMol * structure); bool fragmentSearch(const std::vector aPositionQuery, const std::vector iA1Query, const std::vector iA2Query, const std::vector bondTypesQuery, const std::vector aPositionStructure, const std::vector iA1Structure, const std::vector iA2Structure, const std::vector bondTypesStructure, int nAtomsQuery, int nBondsQuery, int nAtomsStructure, int nBondsStructure); ///Equivalence list generation void equivalenceList(OBMol * pmol, std::vector& eqList); void equivalenceList(const std::vector aPosition,const std::vector aCharge, const std::vector aRad, const std::vector iA1, const std::vector iA2, const std::vector bondTypes, std::vector& eqList, int nAtoms, int nBonds); //Fragment addition void addFragment(OBMol * molecule, OBMol * fragment, int molAN, int fragAN, int molBN, int fragBN, bool isAddition); //routines below have no common meaning, but are necessary to process stereo information void createStereoLists(OBMol * pmol, std::vector& bondStereoList, std::vector& atomStereoList, std::vector& eqList); std::string getAtomMCDL(OBMol * pmol, int ntatoms, const std::vector ix, const std::vector aNumber, const std::vector atomStereoList, const std::vector eqList); std::string getBondMCDL(OBMol * pmol, int nbStore, int ntatoms, const std::vector ix, const std::vector aNumber, int bonds[MAXBONDS][4], const std::vector bondStereoList, const std::vector eqList); void implementAtomStereo(std::vector& iA1, std::vector& iA2, std::vector& stereoBonds, const std::vectorrx, const std::vector ry, int acount, int bcount, std::string astereo); void implementBondStereo(const std::vector iA1, const std::vector iA2, std::vector& rx, std::vector& ry, int acount, int bcount, std::string bstereo); int groupRedraw(OBMol * pmol, int bondN, int atomN, bool atomNInGroup); //int groupRedrawFrameAtom(OBMol * pmol, int bondN, int atomInFrame); int canonizeMCDL(const std::string atomBlock, std::vector & structureList); bool parseFormula(const std::string formulaString, std::vector & enumber, int & valency); void prepareTest(OBMol * pmol, std::ostream & ofs); } // namespace OpenBabel // //! \utilities for MCDL format and other useful utilities osra-2.1.3/src/osra_reaction.cpp0000664000175000017500000006662514115175251015311 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include // std:ostringstream #include // std:string #include // std::vector #include #include #include #include #include #include #include "osra_segment.h" #include "osra_reaction.h" #include "osra_common.h" using namespace OpenBabel; // // Create a reaction representation for input vector of structures // // Parameters: // page_of_structures - input vector of reactants, intermediates and products // output_format - format of the returned result, i.e. rsmi or cmlr // // Returns: // resulting reaction in the format set up by output_format parameter // std::string convert_page_to_reaction( const std::vector &page_of_structures, const std::string &output_format, const std::vector &reactants, const std::vector &products, std::string value, bool reversible) { std::string reaction; OBConversion conv; conv.SetInAndOutFormats(SUBSTITUTE_REACTION_FORMAT,output_format.c_str()); std::ostringstream strstr; if (output_format == "rsmi") { OBReaction react; for (int j=0; j reactant(new OBMol); conv.ReadString(reactant.get(), page_of_structures[reactants[j]]); react.AddReactant(reactant); } for (int j=0; j product(new OBMol); conv.ReadString(product.get(), page_of_structures[products[j]]); react.AddProduct(product); } // react.AddAgent(transition); if (reversible) react.SetReversible(true); trim(value); if (!value.empty()) { OBPairData *label = new OBPairData; label->SetAttribute("OSRA_REACTION_AGENT"); label->SetValue(value.c_str()); react.SetData(label); // react.SetComment(value); react.SetTitle(value); } strstr << conv.WriteString(&react, true); reaction = strstr.str(); return(reaction); } OBMol mol; OBReactionFacade rxnfacade(&mol); for (int j=0; jSetAttribute("OSRA_REACTION_AGENT"); label->SetValue(value.c_str()); mol.SetData(label); // react.SetComment(value); mol.SetTitle(value); } strstr << conv.WriteString(&mol, true); reaction = strstr.str(); return(reaction); } std::string convert_to_smiles_agent_structure(const std::string &structure) { OBConversion conv; conv.SetInAndOutFormats(SUBSTITUTE_REACTION_FORMAT,"smi"); std::string result; OBMol mol; if (conv.ReadString(&mol,structure)) result = conv.WriteString(&mol,true); return result; } void linear_arrow_sort(std::vector &arrows) { point_t start; start.x=0; start.y=0; std::vector new_arrows; while (!arrows.empty()) { double d=FLT_MAX; int closest=0; bool found = false; for (int i=0; i arrows[i].tail.x && new_arrows.back().head.x > new_arrows.back().tail.x && abs(arrows[i].head.y - arrows[i].tail.y)<5 && abs(new_arrows.back().head.y - new_arrows.back().tail.y)<5 && (std::min(arrows[i].head.y,arrows[i].tail.y) - std::max(new_arrows.back().head.y, new_arrows.back().tail.y) > MAX_FONT_HEIGHT || std::min(new_arrows.back().head.y,new_arrows.back().tail.y) - std::max(arrows[i].head.y, arrows[i].tail.y) > MAX_FONT_HEIGHT); //&& arrows[i].tail.x &arrows, const std::vector &page_of_boxes, double max_distance_between_arrows) { for (int j=0; j arrows[j].head.x && page_of_boxes[i].x1 - arrows[j].head.x < max_distance_between_arrows && page_of_boxes[i].y1 < arrows[j].head.y && page_of_boxes[i].y2 > arrows[j].head.y) linebreak = false; arrows[j].linebreak = linebreak; } } void check_linebreak_before(std::vector &arrows, const std::vector &page_of_boxes, double max_distance_between_arrows) { for (int j=0; j arrows[j].tail.y) linebreak_before = false; arrows[j].linebreak_before = linebreak_before; } } void mark_reversible(std::vector &arrows) { if (arrows.size()<2) return; for (int i=0; i0) { x1=arrows[i-1].tail.x; y1=arrows[i-1].tail.y; int k = i+1; while (ka2) { arrows[i].reversible = true; arrows[j].remove=true; } else { arrows[j].reversible = true; arrows[i].remove=true; } } } } std::vector::iterator i=arrows.begin(); while (i!=arrows.end()) { if (i->remove) i=arrows.erase(i); else i++; } } double distance_from_box(const point_t &p, const box_t &b) { if (p.x <= b.x1 && p.y <= b.y1) return(distance(p.x,p.y,b.x1,b.y1)); if (p.x > b.x1 && p.x < b.x2 && p.y < b.y1) return(b.y1-p.y); if (p.x >= b.x2 && p.y <= b.y1) return(distance(p.x,p.y,b.x2,b.y1)); if (p.x < b.x1 && p.y > b.y1 && p.y < b.y2) return(b.x1-p.x); if (p.x > b.x2 && p.y > b.y1 && p.y < b.y2) return(p.x-b.x2); if (p.x <= b.x1 && p.y >= b.y2) return(distance(p.x,p.y,b.x1,b.y2)); if (p.x > b.x1 && p.x < b.x2 && p.y > b.y2) return(p.y-b.y2); if (p.x >= b.x2 && p.y >= b.y2) return(distance(p.x,p.y,b.x2,b.y2)); return 0; } double distance_between_boxes(const box_t &a, const box_t &b) { point_t p; p.x = a.x1; p.y = a.y1; double dab = distance_from_box(p,b); p.x = a.x1; p.y = a.y2; dab = std::min(dab, distance_from_box(p, b)); p.x = a.x2; p.y = a.y1; dab = std::min(dab, distance_from_box(p, b)); p.x = a.x2; p.y = a.y2; dab = std::min(dab, distance_from_box(p, b)); p.x = b.x1; p.y = b.y1; dab = std::min(dab, distance_from_box(p, a)); p.x = b.x2; p.y = b.y1; dab = std::min(dab, distance_from_box(p, a)); p.x = b.x1; p.y = b.y2; dab = std::min(dab, distance_from_box(p, a)); p.x = b.x2; p.y = b.y2; dab = std::min(dab, distance_from_box(p, a)); return(dab); } void sort_boxes_one_by_one(std::vector &b, const std::vector &a, const std::vector &page_of_boxes, bool behind, double max_distance_between_arrows) { box_t p=page_of_boxes[b[0]]; std::set existing; existing.insert(b[0]); if (behind) existing.insert(a[0]); else existing.insert(a.back()); bool found = true; while (found) { found = false; double d = FLT_MAX; int min_j = 0; for (int j=0; j distance_between_boxes(p,page_of_boxes[j]) && existing.find(j) == existing.end()) { d = distance_between_boxes(p,page_of_boxes[j]); min_j = j; } if (d < max_distance_between_arrows) { b.push_back(min_j); p = page_of_boxes[min_j]; found = true; existing.insert(min_j); } } } void sort_boxes_from_arrows(std::vector > &before, const std::vector > &after, const std::vector &page_of_boxes, bool behind, double max_distance_between_arrows) { for (int i=0; i &arrows, const std::vector &page_of_boxes) { double max_distance_between_arrows = MAX_DISTANCE_BETWEEN_ARROWS; for (int j=0; j &arrows, std::vector > &before, const std::vector &page_of_boxes, const std::vector &page_of_structures, double max_distance_between_arrows) { for (int j=0; j=0 && rx1<=l && rx2>=0 && rx2<=l && rx3>=0 && rx3<=l && rx4>=0 && rx4<=l && cr < std::max(page_of_boxes[i].x2 - page_of_boxes[i].x1, page_of_boxes[i].y2 - page_of_boxes[i].y1)) { std::string smi = convert_to_smiles_agent_structure(page_of_structures[i]); if (!smi.empty()) arrows[j].agent += " OSRA_AGENT_SMILES="+smi; } else if (fabs(ry) < std::min(page_of_boxes[i].x2 - page_of_boxes[i].x1, page_of_boxes[i].y2 - page_of_boxes[i].y1) && r arrows[j].head.x && page_of_boxes[i].x2 > maxx && arrows[j].tail.y - page_of_boxes[i].y2 < 2 * (page_of_boxes[i].y2 - page_of_boxes[i].y1)) { i_min_lb = i; maxx = page_of_boxes[i].x2; } } } if (rt < max_distance_between_arrows) { before[j].push_back(i_min); } else if (arrows[j].linebreak_before && i_min_lb >= 0) { before[j].push_back(i_min_lb); } } } void arrange_structures_between_arrows_after( std::vector &arrows, std::vector > &after, const std::vector > &before, const std::vector &page_of_boxes, const std::vector &page_of_structures, double max_distance_between_arrows) { for (int j=0; j= 0 && rx1 <= l && rx2 >= 0 && rx2 <= l && rx3 >= 0 && rx3 <= l && rx4 >= 0 && rx4 <= l && cr < std::max(page_of_boxes[i].x2 - page_of_boxes[i].x1, page_of_boxes[i].y2 - page_of_boxes[i].y1); if (!agent_structure) { if (arrows[j].linebreak) { if (page_of_boxes[i].y1>box_before.y2 && page_of_boxes[i].y1-box_before.y2 < 2*max_distance_between_arrows && ((r l) { rh = r; i_min = i; } } } //std::cout << arrows[j].tail.x << " " << arrows[j].tail.y << " : " << page_of_boxes[i_min].x1 << " " << page_of_boxes[i_min].y1 << " " << rh << std::endl; if (arrows[j].linebreak) { if (rhbox_before.y2 && page_of_boxes[i_min].y1-box_before.y2<2*max_distance_between_arrows) { after[j].push_back(i_min); } } else if (rh > &before, const std::vector &page_of_boxes, const std::vector &pluses, std::vector > &is_plus, double max_distance_between_arrows) { for (int i=0; i=0 && l>=0) { box_t a = page_of_boxes[l]; box_t b = page_of_boxes[k]; for (int m=0; m std::max(a.y1, b.y1) && pluses[m].center.y < std::min(a.y2, b.y2) && ((pluses[m].center.x > a.x2 && pluses[m].center.x < b.x1) || (pluses[m].center.x > b.x2 && pluses[m].center.x < a.x1))) { is_plus[k][l] = m; is_plus[l][k] = m; } // after plus things can be on the next line //d = pluses[m].y - (a.y2 + a.y1)/2; if (pluses[m].center.x>a.x2 && pluses[m].center.y>a.y1 && pluses[m].center.ya.y2 && pluses[m].center.x-a.x2b.x2 && pluses[m].y>b.y1 && pluses[m].yb.y2 && pluses[m].x-b.x2 &arrows, const std::vector &page_of_boxes, const std::vector &pluses, std::vector &results, std::vector &rbox, const std::vector &page_of_structures, const std::string &output_format) { std::vector > before; std::vector > after; if (arrows.empty() || page_of_boxes.empty()) return; // Find average distance between nearest arrows and standard deviation std::vector arrows_by_closest; std::vector dist_arrows; double avg_dist_arrow=0, avg_dist_arrow_squared=0, std_dev_arrow=0; int n_arrows = arrows.size(); point_t start; start.x=0; start.y=0; while (!arrows.empty()) { double d = FLT_MAX; int min_i=0; for (int i=0; i std::min(distance(start.x, start.y, arrows[i].head.x, arrows[i].head.y), distance(start.x, start.y, arrows[i].tail.x, arrows[i].tail.y))) { d = std::min(distance(start.x, start.y, arrows[i].head.x, arrows[i].head.y), distance(start.x, start.y, arrows[i].tail.x, arrows[i].tail.y)); min_i = i; } arrows_by_closest.push_back(arrows[min_i]); dist_arrows.push_back(d); start = arrows[min_i].head; avg_dist_arrow += d; avg_dist_arrow_squared +=d*d; arrows.erase(arrows.begin()+min_i); } avg_dist_arrow /= n_arrows; avg_dist_arrow_squared /= n_arrows; std_dev_arrow = sqrt(avg_dist_arrow_squared - avg_dist_arrow*avg_dist_arrow); mark_reversible(arrows_by_closest); // Break arrows into close-knit groups std::vector > arrow_groups; int i=0; while (i group; group.push_back(arrows_by_closest[i]); i++; while (i > before_group(arrow_groups[i].size()); arrange_structures_between_arrows_before(arrow_groups[i],before_group,page_of_boxes,page_of_structures, max_distance_between_arrows); std::vector > after_group(arrow_groups[i].size()); arrange_structures_between_arrows_after(arrow_groups[i],after_group,before_group,page_of_boxes,page_of_structures, max_distance_between_arrows); /*for (int ii=0;ii>> "; for (int j=0; j>> "; for (int j=0; j > is_plus_before(page_of_boxes.size(), std::vector (page_of_boxes.size(), -1)); arrange_plus_sings_between_boxes(before,page_of_boxes,pluses, is_plus_before, max_distance_between_arrows); std::vector > is_plus_after(page_of_boxes.size(), std::vector (page_of_boxes.size(), -1)); arrange_plus_sings_between_boxes(after,page_of_boxes,pluses, is_plus_after, max_distance_between_arrows); /* for (int ii=0;ii>> "; for (int j=0; j r, p; box_t box; box.x1 = arrows[i].min_x; box.y1 = arrows[i].min_y; box.x2 = arrows[i].max_x; box.y2 = arrows[i].max_y; int ii = before[i].size()-1; while (ii>=0 && before[i][ii]<0) ii--; if (ii>=0 && before[i][ii]>=0) { int k = before[i][ii]; r.push_back(k); box.x1 = std::min(box.x1, page_of_boxes[k].x1); box.y1 = std::min(box.y1, page_of_boxes[k].y1); box.x2 = std::max(box.x2, page_of_boxes[k].x2); box.y2 = std::max(box.y2, page_of_boxes[k].y2); } for (int j=ii-1; j>=0; j--) { int k = before[i][j]; int l = before[i][j+1]; if (k>=0 && l>=0) { int m = is_plus_before[k][l]; if (m>=0) { r.push_back(k); box.x1 = std::min(box.x1, page_of_boxes[k].x1); box.y1 = std::min(box.y1, page_of_boxes[k].y1); box.x2 = std::max(box.x2, page_of_boxes[k].x2); box.y2 = std::max(box.y2, page_of_boxes[k].y2); box.x1 = std::min(box.x1, pluses[m].min_x); box.y1 = std::min(box.y1, pluses[m].min_y); box.x2 = std::max(box.x2, pluses[m].max_x); box.y2 = std::max(box.y2, pluses[m].max_y); } else break; } } if (!after[i].empty()) { ii = 0; while (ii=0) { int k = after[i][ii]; p.push_back(k); box.x1 = std::min(box.x1, page_of_boxes[k].x1); box.y1 = std::min(box.y1, page_of_boxes[k].y1); box.x2 = std::max(box.x2, page_of_boxes[k].x2); box.y2 = std::max(box.y2, page_of_boxes[k].y2); } for (int j=ii+1; j=0 && l>=0) { int m = is_plus_after[k][l]; if (m>=0) { p.push_back(k); box.x1 = std::min(box.x1, page_of_boxes[k].x1); box.y1 = std::min(box.y1, page_of_boxes[k].y1); box.x2 = std::max(box.x2, page_of_boxes[k].x2); box.y2 = std::max(box.y2, page_of_boxes[k].y2); box.x1 = std::min(box.x1, pluses[m].min_x); box.y1 = std::min(box.y1, pluses[m].min_y); box.x2 = std::max(box.x2, pluses[m].max_x); box.y2 = std::max(box.y2, pluses[m].max_y); } else break; } } } if (!r.empty() && !p.empty()) { std::string result = convert_page_to_reaction( page_of_structures,output_format, r, p, arrows[i].agent, arrows[i].reversible); trim(result); if (!result.empty()) { results.push_back(result); rbox.push_back(box); } } } } osra-2.1.3/src/osra_common.h0000664000175000017500000002420414115175251014425 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_common.h // // Common functionality routines // extern "C" { #include } #include "osra.h" #include "osra_segment.h" #include // std::vector // Defines necessary for potrace vectorization functions #define BM_WORDSIZE ((int)sizeof(potrace_word)) #define BM_WORDBITS (8*BM_WORDSIZE) #define BM_HIBIT (((potrace_word)1)<<(BM_WORDBITS-1)) #define bm_scanline(bm, y) ((bm)->map + (y)*(bm)->dy) #define bm_index(bm, x, y) (&bm_scanline(bm, y)[(x)/BM_WORDBITS]) #define bm_mask(x) (BM_HIBIT >> ((x) & (BM_WORDBITS-1))) #define bm_range(x, a) ((int)(x) >= 0 && (int)(x) < (a)) #define bm_safe(bm, x, y) (bm_range(x, (bm)->w) && bm_range(y, (bm)->h)) #define BM_USET(bm, x, y) (*bm_index(bm, x, y) |= bm_mask(x)) #define BM_UCLR(bm, x, y) (*bm_index(bm, x, y) &= ~bm_mask(x)) #define BM_UPUT(bm, x, y, b) ((b) ? BM_USET(bm, x, y) : BM_UCLR(bm, x, y)) #define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0) // // Section: Functions // // Function: get_pixel() // // Returns a binarized pixel value from a gray-level image // // Parameters: // image - image object // bg - gray-level background color // x, y - coordinates of the pixel // THRESHOLD - gray-level threshold for binarization // // Returns: // 1 for set pixel, 0 for background int get_pixel(const Magick::Image &image, const Magick::ColorGray &bg, unsigned int x, unsigned int y, double THRESHOLD); // Function: trim() // // Remove leading and trailing whitespace // // Parameters: // s - string to trim (in/out parameter) void trim(std::string &s); // Function: distance() // // Returns L2 measure between 2 points in 2d space // // Parameters: // x1, y1 - coordinates of the first point // x2,y2 - coordinates of the second point // // Returns: // Distance between points double distance(double x1, double y1, double x2, double y2); // Function: bond_length() // // Returns the length of i-th bond // // Parameters: // bond - vector of bond_t objects in molecule // i - bond number // atom - vector of atom_t objects in molecule // // Returns: // Length of i-th bond double bond_length(const std::vector &bond, int i, const std::vector &atom); // Function: delete_curve() // // Deletes atoms and bonds which belong to a give curve // // Parameters: // atom - vector of atom_t objects in molecule // bond - vector of bond_t objects in molecule // n_atom - last atom in molecule plus one // n_bond last bond in molecule plus one // curve - potrace_path_t pointer denoting a curve which should be deleted // // Returns: // nothing void delete_curve(std::vector &atom, std::vector &bond, int n_atom, int n_bond, const potrace_path_t * const curve); // Function: delete_curve_with_children() // // Same as delete_curve() but also removes all the inner curves // // Parameters: // atom - vector of atom_t objects in molecule // bond - vector of bond_t objects in molecule // n_atom - last atom in molecule plus one // n_bond last bond in molecule plus one // curve - potrace_path_t pointer denoting a curve which should be deleted // // Returns: // nothing void delete_curve_with_children(std::vector &atom, std::vector &bond, int n_atom, int n_bond, const potrace_path_t * const p); // Function: detect_curve() // // Detects if a given curve correspond to any single, simple, non-deleted bonds // // Parameters: // bond - vector of bonds in molecule // n_bond - number of all bonds (last bond plus one) // curve - curve pointer we are checking // // Returns: // True if a single bond corresponding to such curve is found, False otherwise bool detect_curve(std::vector &bond, int n_bond, const potrace_path_t * const curve); // Function: terminal_bond() // // Checks if there exists a bond which has a specified atom as one of the ends and is not a given bond // // Parameters: // a - atom number to check if it belongs to a bond // b - bond number to avoid // bond - vector of bonds in a molecule // n_bond - number of bonds in a molecule // // Returns: // False if a bond ending on atom a is found, True otherwise bool terminal_bond(int a, int b, const std::vector &bond, int n_bond); // Function: bm_new() // // Reserves space for Potrace object for image vectorization // // Parameters: // w - width, h - height // // Returns: // pointer to potrace_bitmap_t potrace_bitmap_t *const bm_new(int w, int h); // Function: angle4() // // Returns cosine of the angle between two vectors given by their end points // // Parameters: // x1,y1, x2,y2 - coordinates of the end points of the first vector // x3,y3, x4, y4 - coordinates of the end points of the second vector // // Returns: // cosine of the angle between two vectors double angle4(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4); // Function: angle_between_bonds() // // Returns cosine of the angle between i-th and j-th bonds in a molecule // // Parameters: // bond - vector of bonds in a molecule // i, j - bonds for which we are calculate the angle // atom - vector of atoms in a molecule // // Returns: // cosine of the angle between bonds i and j double angle_between_bonds(const std::vector &bond, int i, int j, const std::vector &atom); // Function: distance_from_bond_y() // // Returns the length of a normal vector dropped from a point to a line containing the bond // // Parameters: // x0,y0,x1,y1 - coordinates of the bond ends // x,y - coordinates of the point for which we are looking for the distance from the bond // // Returns: // Vertical distance from a point to the bond axis if the coordinates are rotated so that the bond is horizontal double distance_from_bond_y(double x0, double y0, double x1, double y1, double x, double y); // Function: distance_between_bonds() // // Calculates maximum vertical distance from j-th bond ends to bond i. // // Parameters: // bond - vector of bonds in a molecule // i, j - bonds // atom - vector of atoms in a molecule // // Return: // Maximum vertical distance from bond j ends to bond i when the coordinates are rotated so that bond i // is horizontal. If the difference between distances from end a and end b is greater than 4 FLT_MAX // is returned. double distance_between_bonds(const std::vector &bond, int i, int j, const std::vector &atom); // Function: distance_from_bond_x_a() // // Horizontal distance from end a of a bond to a specified point // // Parameters: // x0,y0,x1,y1 - coordinates of bond ends // x, y - coordinates of the specified point // // Returns: // Horizontal component of the distance between point x,y and end a of a bond // when coordinates are rotated so that the bond is horizontal. a->b is the positive direction. double distance_from_bond_x_a(double x0, double y0, double x1, double y1, double x, double y); // Function: distance_from_bond_x_b() // // Horizontal distance from end b of a bond to a specified point // // Parameters: // x0,y0,x1,y1 - coordinates of bond ends // x, y - coordinates of the specified point // // Returns: // Horizontal component of the distance between point x,y and end b of a bond // when coordinates are rotated so that the bond is horizontal. a->b is the positive direction double distance_from_bond_x_b(double x0, double y0, double x1, double y1, double x, double y); // Function: percentile75() // // Returns the bond length at 75% (third quntile) // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // // Returns: // Third quintile value for bond length. double percentile75(const std::vector &bond, int n_bond, const std::vector &atom); // Function: count_pages() // // Counts the number of pages in a specified image file // // Parameters: // input - File name // // Returns: // number of pages in an image file int count_pages(const std::string &input); int count_pages(const Blob &blob); // Function: count_atoms() // // Counts the number of non-deleted atoms in a molecule // // Parameters: // atom - vector of atoms // n_atom - number of atoms // // Returns: // number of non-deleted atoms int count_atoms(const std::vector &atom, int n_atom); // Function: count_bonds() // // Counts the number of non-deleted bonds in a molecule // // Parameters: // bond - vector of bonds // n_bond - number of bonds // // Returns: // number of non-deleted bonds int count_bonds(const std::vector &bond, int n_bond, int &bond_max_type); // Function: load_config_map() // // Loads text file into a std:map structure. Used for loading superatom and spelling files. // // Parameters: // file - file name // out - std:map object with the result // // Returns: // True if file load is successful, False otherwise bool load_config_map(const std::string &file, std::map &out); // Function: comp_boxes() // // Box coordinate comparison function used for sorting molecule-containing boxes // in a top-down left-to-right order // // Parameters: // aa,bb - box_t objects to compare // // Returns: // True if box aa is higher or to the left of box bb. // False otherwise bool comp_boxes(const box_t &aa, const box_t &bb); void debug_image(Image image, const std::vector &atom, int n_atom, const std::vector &bond, int n_bond, const std::string &fname); osra-2.1.3/src/osra_structure.h0000664000175000017500000003554414115175251015206 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_structure.h // // Declares main structure recognition (molecular atoms and bonds) // recognition routines // #include "osra.h" #include "osra_labels.h" //struct: dash_s // used to identify dashed bonds in and small bonds in struct dash_s { //double: x,y //coordinates double x, y; //bool: free // is this dash available for a perspective dashed bond? bool free; //pointer: curve // pointer to the curve found by Potrace const potrace_path_t *curve; //int: area // area occupied by the dash int area; }; //typedef: dash_t //defines dash_t type based on dash_s struct typedef struct dash_s dash_t; struct bracket_t { box_t box; std::string a; }; // // Section: Functions // // Function: remove_disconnected_atoms() // // Removes atoms not connected to any bond // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - number of bonds void remove_disconnected_atoms(std::vector &atom, std::vector &bond, int n_atom, int n_bond); // Function: remove_zero_bonds() // // Removes bonds which either coincide with an existing bond, or have beginning and end atoms the same, or // have one of the atoms non-existing // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms void remove_zero_bonds(std::vector &bond, int n_bond, std::vector &atom); // Function: collapse_doubleup_bonds() // // if a bond coincide with another, remove it and increment the bond type of the existing bond // // Parameters: // bond - vector of bonds // n_bond - number of bonds void collapse_doubleup_bonds(std::vector &bond, int n_bond); // Function: skeletize() // // Collapses two sides of the same bond into a single bond object // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - number of bonds // image - original image // threshold - black-white binarization threshold // bgColor - background color // dist - distance below which it's considered the same bond no matter whether the other conditions are met or not // avg - average bond length // // Returns: // Average bond thickness double skeletize(std::vector &atom, std::vector &bond, int n_bond, const Image &image, double threshold, const ColorGray &bgColor, double dist, double avg); // Function: dist_double_bonds() // // Estimates maximum distance between double bonds // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - number of bonds // avg - average bond length // // Returns: // Estimated distance between double bonds double dist_double_bonds(const std::vector &atom, std::vector &bond, int n_bond, double avg); // Function: double_triple_bonds() // // Detects double and triple bonds from single collinear bonds // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - number of bonds // avg - average bond length // n_atom - number of atoms // max_dist_double_bond - maximum distance between double bonds // // Returns: // New value for n_bond int double_triple_bonds(std::vector &atom, std::vector &bond, int n_bond, double avg, int &n_atom, double max_dist_double_bond); // Function: extend_terminal_bond_to_label() // // Attempt to connect a terminal bond to an atomic label // // Parameters: // atom - vector of atoms // letters - vector of characters // n_letters - number of characters // bond - vector of bonds // n_bond - number of bonds // label - vector of atomic labels // n_label - number of atomic labels // avg - average bond length // maxh - maximum bond thickness // max_dist_double_bond - maximum distance between double bonds void extend_terminal_bond_to_label(std::vector &atom, const std::vector &letters, int n_letters, const std::vector &bond, int n_bond, const std::vector &label, int n_label, double avg, double maxh, double max_dist_double_bond); // Function: extend_terminal_bond_to_bonds() // // Attempts to reconnect a terminal bond to other bonds // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - number of bonds // avg - average bond length // maxh - maximum bond thickness // max_dist_double_bond - maximum distance between double bonds void extend_terminal_bond_to_bonds(std::vector &atom, std::vector &bond, int n_bond, double avg, double maxh, double max_dist_double_bond); // Function: assign_charge() // // Assignes charges to atoms where recognized. is also called at the end. // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - number of bonds // fix - spelling correction map loaded from an external file // superatom - superatom dictionary map loaded from an external file // debug - a flag for debug output void assign_charge(std::vector &atom, std::vector &bond, int n_atom, int n_bond, const std::map &fix, const std::map &superatom, bool debug); // Function: find_atoms() // // Detects atoms and bonds in a vectorized image // // Parameters: // p - output of Potrace vectorization routines // atom - vector of atoms // bond - vector of bonds // n_bond - pointer to the number of bonds // width - image width // height - image height // // Returns: // number of atoms int find_atoms(const potrace_path_t *p, std::vector &atom, std::vector &bond, int *n_bond, int width, int height); // Function: find_dashed_bonds() // // Detects dashed (hash) stereo bonds // // Parameters: // p - output of Potrace vectorization routines // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - pointer to the number of bonds // max - maximum area for a dash // avg - average bond length // img - original image // bg - background color // THRESHOLD - black-white binarization threshold // thick - flag set if the image was subject to thinning // dist - maximum dashed bond length int find_dashed_bonds(const potrace_path_t *p, std::vector &atom, std::vector &bond, int n_atom, int *n_bond, int max, double max_dash_length, const Image &img, const ColorGray &bg, double THRESHOLD, bool thick, double dist, std::vector &letters); // Function: find_small_bonds() // // Bonds which are small in size are flagged as such // // Parameters: // p - Potrace vectorization output // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - pointer to the number of bonds // max_area - maximum area of the bond segment // Small - maximum area of the bond segment which allows not to take into account thickness // thickness - maximum thickness for a small bond // // Returns: // new value of n_atom int find_small_bonds(const potrace_path_t *p, std::vector &atom, std::vector &bond, int n_atom, int *n_bond, double max_area, double Small, double thickness); // Function: resolve_bridge_bonds() // // Detects bridge bonds - bonds which are visually intersecting but have no atom at the point of intersection // // Parameters: // atom - vector of atoms // n_atom - number of atoms // bond - vector of bonds // n_bond - number of bonds // thickness - bond thickness // avg_bond_length - average bond length // supertatom - superatom dictionary map // verbose - print debug info // // Returns: // The number of fragments int resolve_bridge_bonds(std::vector &atom, int n_atom, std::vector &bond, int n_bond, double thickness, double avg_bond_length, const std::map &superatom, bool verbose); // Function: collapse_atoms() // // Atoms found within a threshold distance from each other are collapsed into one // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - number of bonds // dist - threshold distance between atoms void collapse_atoms(std::vector &atom, std::vector &bond, int n_atom, int n_bond, double dist); // Function: collapse_bonds() // // Bond ends with bond length smaller than a threshold are collapsed into one atom // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - number of bonds // dist - threshold bond length void collapse_bonds(std::vector &atom, const std::vector &bond, int n_bond, double dist); // Function: fix_one_sided_bonds() // // T-style bonds are corrected by creating an atom at the intersection point of T // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // thickness - bond thickness // avg - average bond length // // Returns: // new value for n_bond int fix_one_sided_bonds(std::vector &bond, int n_bond, const std::vector &atom, double thickness, double avg); // Function: find_wedge_bonds() // // Detects wedge stereo-bonds // // Parameters: // image - original image // atom - vector of atoms // n_atom - number of atoms // bond - vector of bonds // n_bond - number of bonds // bgColor - background color // THRESHOLD_BOND - black-white binarization threshold // max_dist_double_bond - maximum distance between double bonds // avg - average bond length // limit - minimum difference between the thick and thin ends of the bond // dist - step away from the bond end to measure thickness // // Returns: // Bond thickness double find_wedge_bonds(const Image &image, std::vector &atom, int n_atom, std::vector &bond, int n_bond, const ColorGray &bgColor, double THRESHOLD_BOND, double max_dist_double_bond, double avg, int limit, int dist = 0); // Function: collapse_double_bonds() // // Removes a small bond connecting the ends of a conjoined double bond // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // dist - maximum bond length for small connecting bond void collapse_double_bonds(std::vector &bond, int n_bond, std::vector &atom, double dist); // Function: find_up_down_bonds() // // Detects E/Z stereochemistry around a double bond // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // thickness - bond thickness void find_up_down_bonds(std::vector &bond, int n_bond, std::vector &atom, double thickness); // Function: find_old_aromatic_bonds() // // Detects aromatic bonds which are drawn as a ring with a circle inside // // p - Potrace vectorization output // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // n_atom - number of atoms // avg - average bond length void find_old_aromatic_bonds(const potrace_path_t *p, std::vector &bond, int n_bond, std::vector &atom, int n_atom, double avg); // Function: flatten_bonds() // // "Flattens" (combines into one bond) bonds which have a small "kink" in the middle // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // maxh - maximum vertical distance for the "kink" void flatten_bonds(std::vector &bond, int n_bond, std::vector &atom, double maxh); // Function: mark_terminal_atoms() // // Finds and flags end atoms on a terminal bond // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // n_atom - number of atoms void mark_terminal_atoms(const std::vector &bond, int n_bond, std::vector &atom, int n_atom); // Function: find_limits_on_avg_bond() // // Calculates minimum and maximum bond length consistent with high confidence values // // Parameters: // min_bond - a reference to minimum bond length // max_bond - a reference to maximum bond_length // pages_of_avg_bonds - a vector (element per page) of vectors (element per structure) of average bond lengths // pages_of_ind_conf - a vector (pages) of vectors (structures on a page) of confidence estimes void find_limits_on_avg_bond(double &best_bond, const std::vector > &pages_of_avg_bonds, const std::vector > &pages_of_ind_conf); // Function: find_wavy_bonds() // // Finds undefined stereo bonds (wavy bonds) // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // avg - average bond length // // Returns: number of bonds int find_wavy_bonds(std::vector &bond, int n_bond, const std::vector &atom, double avg); void remove_small_bonds_in_chars(std::vector &atom, std::vector &bond, std::vector &letters); void remove_bracket_atoms(std::vector &atom, int n_atom, const std::vector &bond, int n_bond, const std::set > &brackets, double thickness, int box_x, int box_y, double box_scale, int real_font_width,int real_font_height, std::vector &reduced_bracket_boxes); void assign_labels_to_brackets(std::vector &bracket_boxes, const std::vector &label, int n_label, const std::vector &letters, int n_letters, int real_font_width, int real_font_height); void remove_vertical_bonds_close_to_brackets(const std::vector &bracket_boxes, std::vector &atom, const std::vector &bond, int n_bond, double thickness, double avg_bond_length); void remove_high_order_bonds_connected_to_hash_bonds(std::vector &bond, int n_bond, std::vector &atom, double avg); osra-2.1.3/src/osra_labels.cpp0000664000175000017500000016134414115175251014741 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File: osra_labels.cpp // // Defines functions dealing with atomic labels // #include // fabs(double) #include // FLT_MAX #include // INT_MAX #include // std::ostream, std::cout #include "osra_common.h" #include "osra_ocr.h" #include "osra_labels.h" bool alone(const std::vector &bond, int i, double avg) { bool alone = false; const potrace_path_t * const p = bond[i].curve; if ((p->sign == int('+')) && (p->area < 2 * avg)) alone = true; return (alone); } void delete_bonds_in_char(std::vector &bond, int n_bond, std::vector &atom, double left, double top, double right, double bottom) { for (int j = 0; j < n_bond; j++) if (bond[j].exists && atom[bond[j].a].x >= left && atom[bond[j].a].x <= right && atom[bond[j].a].y >= top && atom[bond[j].a].y <= bottom && atom[bond[j].b].x >= left && atom[bond[j].b].x <= right && atom[bond[j].b].y >= top && atom[bond[j].b].y <= bottom) bond[j].exists = false; } bool chlorine(const std::vector &bond, const std::vector &atom, int i, std::vector &letters, int n_letters, int max_font_height, int min_font_height) { bool res = false; double x = (atom[bond[i].a].x + atom[bond[i].b].x) / 2; double y = (atom[bond[i].a].y + atom[bond[i].b].y) / 2; double r = bond_length(bond, i, atom) / 2; if ((bond_length(bond, i, atom) < max_font_height) && (bond_length(bond, i, atom) > min_font_height) && (fabs( atom[bond[i].a].x - atom[bond[i].b].x) < fabs(atom[bond[i].a].y - atom[bond[i].b].y))) { for (int j = 0; j < n_letters; j++) { if ((distance(x, y, letters[j].x, letters[j].y) < r + letters[j].r) && (fabs(y - letters[j].y) < std::min(r, letters[j].r))) { res = true; } } } return (res); } bool iodine(const std::vector &bond, const std::vector &atom, int i, std::vector &letters, int n_letters, int max_font_height, int min_font_height) { if (bond_length(bond, i, atom) < max_font_height && bond_length(bond, i, atom) > min_font_height && fabs(atom[bond[i].a].x - atom[bond[i].b].x) < V_DISPLACEMENT && fabs(atom[bond[i].a].y - atom[bond[i].b].y) > min_font_height && bond[i].type == 1) { bool found = false; double xa = atom[bond[i].a].x; double ya = atom[bond[i].a].y; double xb = atom[bond[i].b].x; double yb = atom[bond[i].b].y; double bl = bond_length(bond, i, atom); for (int j = 0; j < n_letters; j++) { double d1 = fabs(distance_from_bond_x_a(xa, ya, xb, yb, letters[j].x, letters[j].y)); double d2 = fabs(distance_from_bond_x_b(xa, ya, xb, yb, letters[j].x, letters[j].y)); double h = fabs(distance_from_bond_y(xa, ya, xb, yb, letters[j].x, letters[j].y)); double nb = std::min(d1, d2) - letters[j].r; if (nb <= bl/2 && h <= letters[j].r / 2) found = true; } if (!found) return true; } return false; } int remove_small_bonds(std::vector &bond, int n_bond, const std::vector &atom, std::vector &letters, int n_letters, int max_font_height, int min_font_height, double avg) { for (int i = 0; i < n_bond; i++) if ((bond[i].exists) && (bond[i].type == 1)) { bool al = alone(bond, i, avg); if (bond_length(bond, i, atom) < V_DISPLACEMENT) { bond[i].exists = false; } else if (al) { bool cl = chlorine(bond, atom, i, letters, n_letters, max_font_height, min_font_height); bool io = iodine(bond, atom, i, letters, n_letters, max_font_height, min_font_height); //if (io) //cout<= MAX_ATOMS) n_letters--; bond[i].exists = false; } } } return (n_letters); } bool comp_lbonds(const lbond_t &left, const lbond_t &right) { if (left.x < right.x) return (true); if (left.x > right.x) return (false); return (false); } bool comp_letters(const letters_t &left, const letters_t &right) { if (left.x < right.x) return (true); if (left.x > right.x) return (false); return (false); } int assemble_labels(std::vector &letters, int n_letters, std::vector &label) { std::vector lbond; std::sort(letters.begin(), letters.end(), comp_letters); for (int i = 0; i < letters.size(); i++) { // cout< letters[label[i].n[j]].r / 2) n++; if (n > 1) { std::vector old_label_n(label[i].n); label[i].a = ""; label[i].x1 = FLT_MAX; label[i].x2 = 0; label[i].n.clear(); label_t lb; lb.a = ""; lb.x1 = FLT_MAX; lb.x2 = 0; lb.min_x = INT_MAX; lb.min_y = INT_MAX; lb.max_x = 0; lb.max_y = 0; for (unsigned int j = 0; j < old_label_n.size(); j++) { if (letters[old_label_n[j]].y > cy) { label[i].a += letters[old_label_n[j]].a; label[i].n.push_back(old_label_n[j]); label[i].min_x = std::min(label[i].min_x, letters[old_label_n[j]].min_x); label[i].min_y = std::min(label[i].min_y,letters[old_label_n[j]].min_y); label[i].max_x = std::max(label[i].max_x, letters[old_label_n[j]].max_x); label[i].max_y = std::max(label[i].max_y,letters[old_label_n[j]].max_y); if (isalpha(letters[old_label_n[j]].a)) { if (letters[old_label_n[j]].x < label[i].x1) { label[i].x1 = letters[old_label_n[j]].x; label[i].y1 = letters[old_label_n[j]].y; label[i].r1 = letters[old_label_n[j]].r; } if (letters[old_label_n[j]].x > label[i].x2) { label[i].x2 = letters[old_label_n[j]].x; label[i].y2 = letters[old_label_n[j]].y; label[i].r2 = letters[old_label_n[j]].r; } } } else { lb.a += letters[old_label_n[j]].a; lb.n.push_back(old_label_n[j]); lb.min_x = std::min(lb.min_x, letters[old_label_n[j]].min_x); lb.min_y = std::min(lb.min_y,letters[old_label_n[j]].min_y); lb.max_x = std::max(lb.max_x, letters[old_label_n[j]].max_x); lb.max_y = std::max(lb.max_y,letters[old_label_n[j]].max_y); if (isalpha(letters[old_label_n[j]].a)) { if (letters[old_label_n[j]].x < lb.x1) { lb.x1 = letters[old_label_n[j]].x; lb.y1 = letters[old_label_n[j]].y; lb.r1 = letters[old_label_n[j]].r; } if (letters[old_label_n[j]].x > lb.x2) { lb.x2 = letters[old_label_n[j]].x; lb.y2 = letters[old_label_n[j]].y; lb.r2 = letters[old_label_n[j]].r; } } } } label.push_back(lb); } } for (int i = 0; i < label.size(); i++) { //cout< &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, ColorGray &bgColor, double THRESHOLD, int n_letters) { int max_font_width, max_font_height; std::vector widths, heights; for (int i=0; i= '2' && letters[i].a <= '9') { widths.push_back(letters[i].max_x - letters[i].min_x+1); heights.push_back(letters[i].max_y - letters[i].min_y+1); } if (widths.size() < 3) return n_letters; max_font_width = std::min(MAX_FONT_WIDTH, widths[widths.size() / 2]); max_font_height = std::min(MAX_FONT_HEIGHT, heights[heights.size() / 2]); int n, *tag; potrace_dpoint_t (*c)[3]; while (p != NULL) { if ((p->sign == int('+'))) { n = p->curve.n; tag = p->curve.tag; c = p->curve.c; int top = height; int x1 = 0; int left = width; int y1 = 0; int bottom = 0; int x2 = 0; int right = 0; int y2 = 0; int cx,cy; for (int i = 0; i < n; i++) { switch (tag[i]) { case POTRACE_CORNER: cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; case POTRACE_CURVETO: cx = c[i][0].x; cy = c[i][0].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; } cx = c[i][2].x; cy = c[i][2].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } } if (((bottom - top) <= max_font_height) && ((right - left) <= max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT)) { int s = 1; while ((top > 0) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, x1, top, THRESHOLD); if (s > 0) top--; } s = 1; while ((bottom < height) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, x2, bottom, THRESHOLD); if (s > 0) bottom++; } s = 1; while ((left > 0) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, left, y1, THRESHOLD); if (s > 0) left--; } s = 1; while ((right < width) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, right, y2, THRESHOLD); if (s > 0) right++; } } if (((bottom - top) <= max_font_height) && ((right - left) <= max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT)) { bool found = false; for (int i=0; i= MAX_ATOMS) n_letters--; delete_bonds_in_char(bond, n_bond, atom, left, top, right, bottom); delete_curve_with_children(atom, bond, n_atom, n_bond, p); } } } } p = p->next; } return (n_letters); } int find_chars(const potrace_path_t * p, const Image &orig, std::vector &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, ColorGray &bgColor, double THRESHOLD, int max_font_width, int max_font_height, int &real_font_width, int &real_font_height, bool verbose, const std::string &recognized_chars) { int n, *tag, n_letters = 0; potrace_dpoint_t (*c)[3]; real_font_width = 0; real_font_height = 0; while (p != NULL) { if ((p->sign == int('+'))) { n = p->curve.n; tag = p->curve.tag; c = p->curve.c; int top = height; int x1 = 0; int left = width; int y1 = 0; int bottom = 0; int x2 = 0; int right = 0; int y2 = 0; int cx,cy; for (int i = 0; i < n; i++) { switch (tag[i]) { case POTRACE_CORNER: cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; case POTRACE_CURVETO: cx = c[i][0].x; cy = c[i][0].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; } cx = c[i][2].x; cy = c[i][2].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } } if (((bottom - top) <= 2 * max_font_height) && ((right - left) <= 2 * max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT)) { int s = 1; while ((top > 0) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, x1, top, THRESHOLD); if (s > 0) top--; } s = 1; while ((bottom < height) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, x2, bottom, THRESHOLD); if (s > 0) bottom++; } s = 1; while ((left > 0) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, left, y1, THRESHOLD); if (s > 0) left--; } s = 1; while ((right < width) && (s > 0)) { s = 0; s = get_pixel(orig, bgColor, right, y2, THRESHOLD); if (s > 0) right++; } } bool found = false; if (((bottom - top) <= max_font_height) && ((right - left) <= max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT)) { char label = 0; label = get_atom_label(orig, bgColor, left, top, right, bottom, THRESHOLD, (right + left) / 2, top, false, verbose, false, recognized_chars); if (label != 0) { letters_t lt; letters.push_back(lt); letters[n_letters].a = label; letters[n_letters].x = (left + right) / 2; letters[n_letters].y = (top + bottom) / 2; letters[n_letters].r = distance(left, top, right, bottom) / 2; letters[n_letters].min_x = left; letters[n_letters].max_x = right; letters[n_letters].min_y = top; letters[n_letters].max_y = bottom; letters[n_letters].curve = p; if (right - left > real_font_width) real_font_width = right - left; if (bottom - top > real_font_height) real_font_height = bottom - top; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; delete_bonds_in_char(bond, n_bond, atom, left, top, right, bottom); delete_curve_with_children(atom, bond, n_atom, n_bond, p); found = true; } } if (((bottom - top) <= 2 * max_font_height) && ((right - left) <= max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT) && !found) { char label1 = 0; int newtop = (top + bottom) / 2; label1 = get_atom_label(orig, bgColor, left, newtop, right, bottom, THRESHOLD, (right + left) / 2, newtop, false, verbose, false, recognized_chars); char label2 = 0; int newbottom = (top + bottom) / 2; label2 = get_atom_label(orig, bgColor, left, top, right, newbottom, THRESHOLD, (right + left) / 2, top, false, verbose, false, recognized_chars); if (label1 != 0 && label2 != 0 && !(tolower(label1) == 's' && tolower(label2) =='s')) { //cout << label1 << label2 << endl; letters_t lt1; letters.push_back(lt1); letters[n_letters].a = label1; letters[n_letters].x = (left + right) / 2; letters[n_letters].y = (newtop + bottom) / 2; letters[n_letters].r = distance(left, newtop, right, bottom) / 2; letters[n_letters].min_x = left; letters[n_letters].max_x = right; letters[n_letters].min_y = newtop; letters[n_letters].max_y = bottom; letters[n_letters].curve = p; if (right - left > real_font_width) real_font_width = right - left; if (bottom - newtop > real_font_height) real_font_height = bottom - newtop; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; letters_t lt2; letters.push_back(lt2); letters[n_letters].a = label2; letters[n_letters].x = (left + right) / 2; letters[n_letters].y = (top + newbottom) / 2; letters[n_letters].r = distance(left, top, right, newbottom) / 2; letters[n_letters].min_x = left; letters[n_letters].max_x = right; letters[n_letters].min_y = top; letters[n_letters].max_y = newbottom; letters[n_letters].curve = p; if (newbottom - top > real_font_height) real_font_height = newbottom - top; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; delete_bonds_in_char(bond, n_bond, atom, left, top, right, bottom); delete_curve_with_children(atom, bond, n_atom, n_bond, p); found = true; } } if (((bottom - top) <= max_font_height) && ((right - left) <= 2 * max_font_width) && (right - left > V_DISPLACEMENT) && (bottom - top > MIN_FONT_HEIGHT) && !found) { char label1 = 0; int newright = (left + right) / 2; label1 = get_atom_label(orig, bgColor, left, top, newright, bottom, THRESHOLD, (left + newright) / 2, top, false, verbose, false, recognized_chars); char label2 = 0; int newleft = (left + right) / 2; label2 = get_atom_label(orig, bgColor, newleft, top, right, bottom, THRESHOLD, (newleft + right) / 2, top, false, verbose, false, recognized_chars); if ((label1 != 0) && (label2 != 0)) { //cout << label1 << label2 << endl; letters_t lt1; letters.push_back(lt1); letters[n_letters].a = label1; letters[n_letters].x = (left + newright) / 2; letters[n_letters].y = (top + bottom) / 2; letters[n_letters].r = distance(left, top, newright, bottom) / 2; letters[n_letters].min_x = left; letters[n_letters].max_x = newright; letters[n_letters].min_y = top; letters[n_letters].max_y = bottom; letters[n_letters].curve = p; if (newright - left > real_font_width) real_font_width = newright - left; if (bottom - top > real_font_height) real_font_height = bottom - top; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; letters_t lt2; letters.push_back(lt2); letters[n_letters].a = label2; letters[n_letters].x = (newleft + right) / 2; letters[n_letters].y = (top + bottom) / 2; letters[n_letters].r = distance(newleft, top, right, bottom) / 2; letters[n_letters].min_x = newleft; letters[n_letters].max_x = right; letters[n_letters].min_y = top; letters[n_letters].max_y = bottom; letters[n_letters].curve = p; if (right - newleft > real_font_width) real_font_width = right - newleft; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; delete_bonds_in_char(bond, n_bond, atom, left, top, right, bottom); delete_curve_with_children(atom, bond, n_atom, n_bond, p); found = true; } } } p = p->next; } if (real_font_width < 1) real_font_width = max_font_width; else real_font_width++; if (real_font_height < 1) real_font_height = max_font_height; else real_font_height++; return (n_letters); } int find_fused_chars(std::vector &bond, int n_bond, std::vector &atom, std::vector &letters, int n_letters, int max_font_height, int max_font_width, char dummy, const Image &orig, const ColorGray &bgColor, double THRESHOLD, unsigned int size, bool verbose, const std::string &recognized_chars) { double dist = std::max(max_font_width, max_font_height); for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond_length(bond, i, atom) < dist) { std::list t; t.push_back(i); double xmin1 = std::min(atom[bond[i].a].x, atom[bond[i].b].x); double xmax1 = std::max(atom[bond[i].a].x, atom[bond[i].b].x); double ymin1 = std::min(atom[bond[i].a].y, atom[bond[i].b].y); double ymax1 = std::max(atom[bond[i].a].y, atom[bond[i].b].y); for (int j = 0; j < n_bond; j++) if (bond[j].exists && bond_length(bond, j, atom) < dist && j != i && atom[bond[j].a].x >= xmin1 && atom[bond[j].a].x >= xmin1) { double xmax2 = std::max(xmax1, std::max(atom[bond[j].a].x, atom[bond[j].b].x)); double ymin2 = std::min(ymin1, std::min(atom[bond[j].a].y, atom[bond[j].b].y)); double ymax2 = std::max(ymax1, std::max(atom[bond[j].a].y, atom[bond[j].b].y)); if (xmax2 - xmin1 <= max_font_width && ymax2 - ymin2 <= max_font_height) t.push_back(j); } std::vector all_bonds(n_bond, 0); for (int j = 0; j < n_bond; j++) if (bond[j].exists) all_bonds[j] = 1; std::list bag1, bag2; all_bonds[i] = 2; bag1.push_back(i); while (!bag1.empty()) { int k = bag1.front(); bag1.pop_front(); all_bonds[k] = 3; for (int j = 0; j < n_bond; j++) if (j != k && all_bonds[j] == 1 && (bond[k].a == bond[j].a || bond[k].a == bond[j].b || bond[k].b == bond[j].a || bond[k].b == bond[j].b)) { all_bonds[j] = 2; bag1.push_back(j); } } while (!t.empty()) { int k = t.front(); t.pop_front(); if (all_bonds[k] == 3) bag2.push_back(k); } unsigned int bag_size = bag2.size(); if (bag_size > size) { double cx = 0; double cy = 0; int n = 0; double l = FLT_MAX, r = 0, t = FLT_MAX, b = 0; while (!bag2.empty()) { int k = bag2.front(); bag2.pop_front(); cx += atom[bond[k].a].x + atom[bond[k].b].x; cy += atom[bond[k].a].y + atom[bond[k].b].y; l = std::min(l, std::min(atom[bond[k].a].x, atom[bond[k].b].x)); r = std::max(r, std::max(atom[bond[k].a].x, atom[bond[k].b].x)); t = std::min(t, std::min(atom[bond[k].a].y, atom[bond[k].b].y)); b = std::max(b, std::max(atom[bond[k].a].y, atom[bond[k].b].y)); n += 2; } cx /= n; cy /= n; if (r - l > MIN_FONT_HEIGHT && b - t > MIN_FONT_HEIGHT) { int left = int(cx - max_font_width / 2) - 1; int right = int(cx + max_font_width / 2) - 1; int top = int(cy - max_font_height / 2); int bottom = int(cy + max_font_height / 2); char label = 0; if (dummy != 0) { label = dummy; //cout << bag_size << " " << left << " " << top << " " << right << " " << bottom << endl; } else { label = get_atom_label(orig, bgColor, left, top, right, bottom, THRESHOLD, (left + right) / 2, top, false, verbose, false, recognized_chars); } if ((label != 0 && label != 'P' && label != 'p' && label != 'F' && label != 'X' && label != 'Y' && label != 'n' && label != 'F' && label != 'U' && label != 'u' && label != 'h') || dummy != 0) { bool overlap = false; for (int j = 0; j < n_letters; j++) { if (distance((left + right) / 2, (top + bottom) / 2, letters[j].x, letters[j].y) < letters[j].r) overlap = true; } if (!overlap) { letters_t lt; letters.push_back(lt); letters[n_letters].a = label; letters[n_letters].x = (left + right) / 2; letters[n_letters].y = (top + bottom) / 2; letters[n_letters].r = distance(left, top, right, bottom) / 2; letters[n_letters].min_x = l; letters[n_letters].max_x = r; letters[n_letters].min_y = t; letters[n_letters].max_y = b; letters[n_letters].free = true; n_letters++; if (n_letters >= MAX_ATOMS) n_letters--; } delete_bonds_in_char(bond, n_bond, atom, left, top, right, bottom); } } } } return (n_letters); } void detect_plus_minus(const Image &image, ColorGray &bg, double THRESHOLD, int x1, int x2, int y1, int y2, int top, int left, int right, int bottom, bool &is_plus, bool &is_minus) { std::vector > pic(right-left+1, std::vector (bottom-top+1,0)); for (int i=left; i<=right; i++) for (int j=top; j<=bottom; j++) pic[i-left][j-top] = get_pixel(image, bg, i, j, THRESHOLD) ; int y = 0; int x = x1 - left; if (x<0 || x>=pic.size()) return; while (y=pic[x].size()) return; std::vector points; // populating points with BFS std::list bag; point_t p; p.x = x; p.y = y; bag.push_back(p); pic[p.x][p.y] = 2; while (!bag.empty()) { point_t c = bag.front(); bag.pop_front(); points.push_back(c); pic[c.x][c.y] = 0; // this goes around 3x3 square touching the chosen pixel for (int i = c.x - 1; i < c.x + 2; i++) for (int j = c.y - 1; j < c.y + 2; j++) if (i >= 0 && j >= 0 && i < pic.size() && j < pic[i].size() && pic[i][j] == 1) { point_t t; t.x = i; t.y = j; bag.push_back(t); pic[i][j] = 2; } } const int len=50; std::vector hist(len,0); int top_pos=0; int top_value=0; point_t head, tail,center; int min_x, min_y, max_x, max_y; build_hist(points,hist,len,top_pos,top_value,head,tail,center,min_x, min_y, max_x, max_y); double fill = (double) points.size() / ((max_x-min_x+1)*(max_y-min_y+1)); double aspect = (double) (max_y - min_y +1) / (max_x - min_x +1); if (points.size() > len) { std::vector peaks(1,top_pos); std::vector values(1,top_value); for (int k=1; k=len) pos -= len; int after=pos+1; int before=pos-1; if (after>=len) after -=len; if (before<0) before +=len; if (hist[before]=top_value/2) // find all peaks at least half as high as the top-most { peaks.push_back(pos); values.push_back(hist[pos]); } } if (peaks.size() == 2 && abs(len/2 - abs(peaks[1]-peaks[0]))<=1) // only two peaks are present at 180 degrees { if (aspect < 0.7 && fill > 0.9) is_minus = true; } if (peaks.size() == 4 && (double(values[1])/values[0]>0.8 || values[0]-values[1]<=2) && (double(values[2])/values[0]>0.8 || values[0]-values[2]<=2) && (double(values[3])/values[0]>0.8 || values[0]-values[3]<=2)) { bool first=false, second=false, third=false, fourth=false; for (int j=0; j<4; j++) { if (peaks[j] <= 1 || peaks[j] >=len-2) first=true; if (abs(len/4-peaks[j])<=1) second=true; if (abs(len/2-peaks[j])<=1) third=true; if (abs(3*len/4-peaks[j])<=1) fourth=true; } for (int j=0; j=len) kk -=len; hist[kk]=0; } bool low=true; for(int k=0; k3) low=false; if (first && second && third && fourth && low) { // we found a plus! is_plus = true; } } } else { if (aspect < 0.7 && fill > 0.9) is_minus = true; if (aspect > 0.7 && aspect < 1. / 0.7 && abs(y1 - y2) < 3 && abs(y1 + y2 - bottom - top) / 2 < 3 && abs(x1 - x2) < 3 && abs(x1 + x2 - right - left) / 2 < 3) is_plus = true; } } int find_plus_minus(const potrace_path_t *p, const Image &image, ColorGray &bgColor, double THRESHOLD, std::vector &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, int max_font_height, int max_font_width, int n_letters, double avg_bond_length) { int n, *tag; potrace_dpoint_t (*c)[3]; while (p != NULL) { if ((p->sign == int('+')) && detect_curve(bond, n_bond, p)) { n = p->curve.n; tag = p->curve.tag; c = p->curve.c; int top = height; int x1 = 0; int left = width; int y1 = 0; int bottom = 0; int x2 = 0; int right = 0; int y2 = 0; int cx,cy; for (int i = 0; i < n; i++) { switch (tag[i]) { case POTRACE_CORNER: cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; case POTRACE_CURVETO: cx = c[i][0].x; cy = c[i][0].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } break; } cx = c[i][2].x; cy = c[i][2].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; if (cx < left) { left = cx; y1 = cy; } if (cx > right) { right = cx; y2 = cy; } if (cy < top) { top = cy; x1 = cx; } if (cy > bottom) { bottom = cy; x2 = cx; } } if (((bottom - top) <= max_font_height) && ((right - left) <= max_font_width) && (right - left > 1)) { char c = ' '; bool char_to_right = false; bool inside_char = false; for (int j = 0; j < n_letters; j++) { if (letters[j].x > right && (top + bottom) / 2 > letters[j].min_y && (top + bottom) / 2 < letters[j].max_y && right > letters[j].min_x - letters[j].r && letters[j].a != '-' && letters[j].a != '+') char_to_right = true; if (letters[j].min_x <= left && letters[j].max_x >= right && letters[j].min_y <= top && letters[j].max_y >= bottom) inside_char = true; } bool is_minus = false; bool is_plus = false; detect_plus_minus(image,bgColor, THRESHOLD,x1,x2,y1,y2,top,left,right,bottom,is_plus,is_minus); if (is_minus && !char_to_right && !inside_char && (right - left) < avg_bond_length/2) { c = '-'; // cout<<"Minus: "<<(right-left)<<" "<= MAX_ATOMS) n_letters--; delete_curve_with_children(atom, bond, n_atom, n_bond, p); } } } p = p->next; } return (n_letters); } int clean_unrecognized_characters(std::vector &bond, int n_bond, const std::vector &atom, int real_font_height, int real_font_width, unsigned int size, std::vector &letters, int n_letters) { std::vector all_bonds(n_bond, 0); for (int i = 0; i < n_bond; i++) if (bond[i].exists) all_bonds[i] = 1; for (int i = 0; i < n_bond; i++) if (all_bonds[i] == 1) { std::list bag1, bag2; std::list trash; all_bonds[i] = 2; bag1.push_back(i); while (!bag1.empty()) { int k = bag1.front(); bag1.pop_front(); all_bonds[k] = 0; bag2.push_back(k); for (int j = 0; j < n_bond; j++) if (j != k && all_bonds[j] == 1 && (bond[k].a == bond[j].a || bond[k].a == bond[j].b || bond[k].b == bond[j].a || bond[k].b == bond[j].b)) { all_bonds[j] = 2; bag1.push_back(j); } } double t = FLT_MAX, b = 0, l = FLT_MAX, r = 0; while (!bag2.empty()) { int k = bag2.front(); bag2.pop_front(); trash.push_back(k); if (atom[bond[k].a].x < l) l = atom[bond[k].a].x; if (atom[bond[k].b].x < l) l = atom[bond[k].b].x; if (atom[bond[k].a].x > r) r = atom[bond[k].a].x; if (atom[bond[k].b].x > r) r = atom[bond[k].b].x; if (atom[bond[k].a].y < t) t = atom[bond[k].a].y; if (atom[bond[k].b].y < t) t = atom[bond[k].b].y; if (atom[bond[k].a].y > b) b = atom[bond[k].a].y; if (atom[bond[k].b].y > b) b = atom[bond[k].b].y; } if ((r - l) < real_font_width && (b - t) < real_font_height && trash.size() > size) { while (!trash.empty()) { int k = trash.front(); trash.pop_front(); bond[k].exists = false; } letters_t lt; letters.push_back(lt); letters[n_letters].a = '*'; letters[n_letters].x = (l + r) / 2; letters[n_letters].y = (t + b) / 2; letters[n_letters].r = distance(l, t, r, b) / 2; letters[n_letters].min_x = l; letters[n_letters].max_x = r; letters[n_letters].min_y = t; letters[n_letters].max_y = b; letters[n_letters].free = true; // cout<= MAX_ATOMS) n_letters--; } } return (n_letters); } void remove_small_terminal_bonds(std::vector &bond, int n_bond, std::vector &atom, double avg) { bool found = true; while (found) { found = false; for (int j = 0; j < n_bond; j++) if (bond[j].exists && bond[j].type == 1 && !bond[j].wedge && !bond[j].hash && !bond[j].arom && bond_length(bond, j, atom) < avg / 2) { bool not_corner_a = terminal_bond(bond[j].a, j, bond, n_bond); bool not_corner_b = terminal_bond(bond[j].b, j, bond, n_bond); if (not_corner_a) { bond[j].exists = false; atom[bond[j].a].exists = false; found = true; if (atom[bond[j].b].label == " ") { if (atom[bond[j].a].label != " ") { atom[bond[j].b].label = atom[bond[j].a].label; atom[bond[j].b].min_x = std::min(atom[bond[j].b].min_x, atom[bond[j].a].min_x); atom[bond[j].b].min_y = std::min(atom[bond[j].b].min_y, atom[bond[j].a].min_y); atom[bond[j].b].max_x = std::max(atom[bond[j].b].max_x, atom[bond[j].a].max_x); atom[bond[j].b].max_y = std::max(atom[bond[j].b].max_y, atom[bond[j].a].max_y); } else { bool dashed = false; int n = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists && i != j && (bond[i].a == bond[j].b || bond[i].b == bond[j].b)) { n++; if (bond[i].hash) dashed = true; } if (!dashed) { atom[bond[j].b].label = "Xx"; atom[bond[j].b].min_x = std::min(atom[bond[j].b].min_x, atom[bond[j].a].min_x); atom[bond[j].b].min_y = std::min(atom[bond[j].b].min_y, atom[bond[j].a].min_y); atom[bond[j].b].max_x = std::max(atom[bond[j].b].max_x, atom[bond[j].a].max_x); atom[bond[j].b].max_y = std::max(atom[bond[j].b].max_y, atom[bond[j].a].max_y); } } } } if (not_corner_b) { bond[j].exists = false; atom[bond[j].b].exists = false; found = true; if (atom[bond[j].a].label == " ") { if (atom[bond[j].b].label != " ") { atom[bond[j].a].label = atom[bond[j].b].label; atom[bond[j].a].min_x = std::min(atom[bond[j].b].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[j].b].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::max(atom[bond[j].b].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::max(atom[bond[j].b].max_y, atom[bond[j].a].max_y); } else { bool dashed = false; int n = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists && i != j && (bond[i].a == bond[j].a || bond[i].b == bond[j].a)) { n++; if (bond[i].hash) dashed = true; } if (!dashed) { atom[bond[j].a].label = "Xx"; atom[bond[j].a].min_x = std::min(atom[bond[j].b].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[j].b].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::max(atom[bond[j].b].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::max(atom[bond[j].b].max_y, atom[bond[j].a].max_y); } } } } } } } osra-2.1.3/src/osra_stl.h0000664000175000017500000000360214115175251013736 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_stl.h // // STL Helpers // #include // std::vector #include // std::ostream #include "osra_labels.h" #include "osra_fragments.h" // Function: operator<<() // // Helper template method to print vectors. namespace std { std::ostream& operator<<(std::ostream &os, const letters_t &letter); std::ostream& operator<<(std::ostream &os, const label_t &label); std::ostream& operator<<(std::ostream &os, const atom_t &atom); std::ostream& operator<<(std::ostream &os, const bond_t &bond); std::ostream& operator<<(std::ostream &os, const fragment_t &fragment); template std::ostream& operator<<(std::ostream &os, const std::vector &v) { os << '['; if (!v.empty()) { typedef typename std::vector::const_iterator const_iterator; const_iterator last = v.end(); std::copy(v.begin(), --last, std::ostream_iterator(os, ", ")); os << *last; } os << ']'; return os; } } osra-2.1.3/src/osra_segment.cpp0000664000175000017500000011740414115175251015137 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File: osra_segment.cpp // // Defines page segmentation functions // #include // std::ostream, std::cout #include "osra.h" #include "osra_common.h" #include "osra_segment.h" #include "osra_labels.h" #include "osra_ocr.h" unsigned int distance_between_points(const point_t &p1, const point_t &p2) { return std::max(abs(p1.x - p2.x), abs(p1.y - p2.y)); } unsigned int distance_between_segments(const std::vector &s1, const std::vector &s2) { int r = INT_MAX; int d; for (std::vector::const_iterator i = s1.begin(); i != s1.end(); i++) for (std::vector::const_iterator j = s2.begin(); j != s2.end(); j++) { d = distance_between_points(*i, *j); if (d < r) r = d; } /* unsigned int ii, jj; #pragma omp parallel { int priv_min = INT_MAX; #pragma omp for for (ii = 0; ii < s1.size(); ii++) { for (jj = 0; jj < s2.size(); jj++) { int d = distance_between_points(s1[ii], s2[jj]); if (d < priv_min) priv_min = d; } } //#pragma omp flush (r) if (priv_min < r) { #pragma omp critical { if (priv_min < r) r = priv_min; } } } */ return r; } void find_connected_components(const Image &image, double threshold, const ColorGray &bgColor, std::vector > &segments, std::vector > &margins, bool adaptive) { point_t p; std::list points; int speckle_area = 2; if (adaptive) { int speckle_side = std::min(image.columns(), image.rows()) / 200; speckle_area = speckle_side * speckle_side; if (speckle_area < 2) speckle_area = 2; } std::vector > tmp(image.columns(), std::vector (image.rows(), 0)); for (unsigned int i = 0; i < image.columns(); i++) for (unsigned int j = 0; j < image.rows(); j++) if (get_pixel(image, bgColor, i, j, threshold) == 1) // populate with low threshold for future anisotropic smoothing tmp[i][j] = 1; for (unsigned int i = 0; i < image.columns(); i++) for (unsigned int j = 0; j < image.rows(); j++) if (tmp[i][j] == 1) { tmp[i][j] = 2; p.x = i; p.y = j; points.push_back(p); std::list new_segment; std::vector new_margin; int counter = 0; point_t p1; while (!points.empty()) { p = points.back(); points.pop_back(); new_segment.push_back(p); tmp[p.x][p.y] = -1; bool on_the_margin = false; // "k" should be in range "[0 .. image.columns) intercepted with [p.x - 1 .. p.x + 2)" ==> "p.x + 2" should be positive ==> "p.x >= -1" // "l" should be in range "[0 .. image.rows) intercepted with [p.y - 1 .. p.y + 2)" ==> "p.y + 2" should be positive ==> "p.y >= -1" if (p.x >= -1 && p.y >= -1) { unsigned int x_lower = p.x > 1 ? p.x - 1 : 0; // "k" cannot be less then zero unsigned int y_lower = p.y > 1 ? p.y - 1 : 0; // "l" cannot be less then zero unsigned int x_upper = p.x + 2; if (x_upper > image.columns()) x_upper = image.columns(); unsigned int y_upper = p.y + 2; if (y_upper > image.rows()) y_upper = image.rows(); for (int k = x_lower; k < x_upper; k++) for (int l = y_lower; l < y_upper; l++) { if (tmp[k][l] == 1) { p1.x = k; p1.y = l; points.push_back(p1); tmp[k][l] = 2; } else if ((int) k != p.x && (int) l != p.y && tmp[k][l] == 0) { on_the_margin = true; } } } if (on_the_margin && (new_margin.size() < PARTS_IN_MARGIN || (counter % PARTS_IN_MARGIN) == 0)) new_margin.push_back(p); if (on_the_margin) counter++; } if (segments.size() > MAX_SEGMENTS) return; if (new_segment.size() > speckle_area) { segments.push_back(new_segment); margins.push_back(new_margin); } } } unsigned int area_ratio(unsigned int a, unsigned int b) { double r = std::max(a, b) / std::min(a, b); return (unsigned int) r; } void build_distance_matrix(const std::vector > &margins, unsigned int max_dist, std::vector > &distance_matrix, std::vector > &features, const std::vector > &segments, unsigned int max_area_ratio, std::vector > &area_matrix) { unsigned int d; unsigned int ar; for (unsigned int s1 = 0; s1 < margins.size(); s1++) for (unsigned int s2 = s1 + 1; s2 < margins.size(); s2++) if (distance_between_points(margins[s1].front(), margins[s2].front()) < (PARTS_IN_MARGIN * margins[s1].size() + PARTS_IN_MARGIN * margins[s2].size()) / 2 + max_dist) { d = distance_between_segments(margins[s1], margins[s2]); if (d < max_dist) { distance_matrix[s1][s2] = d; distance_matrix[s2][s1] = d; ar = area_ratio(segments[s1].size(), segments[s2].size()); //cout << ar << endl; area_matrix[s1][s2] = ar; area_matrix[s2][s1] = ar; if (ar < max_area_ratio && d < max_dist) features[ar][d]++; } } } void build_explicit_clusters( const std::list > &clusters, const std::vector > &segments, std::list > > &explicit_clusters) { explicit_clusters.clear(); for (std::list >::const_iterator c = clusters.begin(); c != clusters.end(); c++) { std::list > set_of_segments; for (std::list::const_iterator s = c->begin(); s != c->end(); s++) if (!segments[*s].empty()) set_of_segments.push_back(segments[*s]); if (!set_of_segments.empty()) explicit_clusters.push_back(set_of_segments); } } void remove_separators(std::vector > &segments, std::vector > &margins, double max_aspect, unsigned int size) { std::vector >::iterator s; std::vector >::iterator m; s = segments.begin(); m = margins.begin(); while (s != segments.end() && m != margins.end()) { if (s->size() <= size) { s++; m++; continue; } int stop = INT_MAX, sleft = INT_MAX, sbottom = 0, sright = 0; for (std::list::iterator p = s->begin(); p != s->end(); p++) { if (p->x < sleft) sleft = p->x; if (p->x > sright) sright = p->x; if (p->y < stop) stop = p->y; if (p->y > sbottom) sbottom = p->y; } double aspect = 0; if (sright != sleft) aspect = 1. * (sbottom - stop+1) / (sright - sleft+1); // where did right and left come from? if (aspect > max_aspect || aspect < 1. / max_aspect) { s = segments.erase(s); m = margins.erase(m); } else { s++; m++; } } } void remove_tables_old(std::vector > &segments, std::vector > &margins, unsigned int size) { std::vector >::iterator s; std::vector >::iterator m; s = segments.begin(); m = margins.begin(); while (s != segments.end() && m != margins.end()) { if (m->size() <= size) { s++; m++; continue; } int top = INT_MAX, left = INT_MAX, bottom = 0, right = 0; int border_count = 0; for (std::vector::iterator p = m->begin(); p != m->end(); p++) { if (p->x < left) left = p->x; if (p->x > right) right = p->x; if (p->y < top) top = p->y; if (p->y > bottom) bottom = p->y; } double aspect = FLT_MAX; if (right != left) aspect = 1. * (bottom - top) / (right - left); if (aspect >= MAX_ASPECT || aspect <= MIN_ASPECT) { s++; m++; continue; } for (std::vector::iterator p = m->begin(); p != m->end(); p++) if (p->x - left < 2 || right - p->x < 2 || p->y - top < 2 || bottom - p->y < 2) { border_count++; } //cout << border_count << " " << 2 * (right - left) + 2 * (bottom - top) << endl; if (border_count > BORDER_COUNT) { s = segments.erase(s); m = margins.erase(m); } else { s++; m++; } } } double cos_angle_between_points(point_t a, point_t b, point_t c) { double v1_x = a.x-b.x; double v1_y = a.y-b.y; double v2_x = c.x-b.x; double v2_y = c.y-b.y; double v1 = sqrt(v1_x*v1_x+v1_y*v1_y); double v2 = sqrt(v2_x*v2_x+v2_y*v2_y); if (v1>0 && v2>0) return (v1_x*v2_x+v1_y*v2_y)/(v1*v2); else return FLT_MAX; } // clockwise actual angle is considered positive std::pair find_rotation(point_t top, point_t left, point_t bottom, point_t right) { double top_angle = fabs(cos_angle_between_points(left,top,right)); double right_angle = fabs(cos_angle_between_points(top,right,bottom)); double bottom_angle = fabs(cos_angle_between_points(right,bottom,left)); double left_angle = fabs(cos_angle_between_points(bottom,left,top)); double dx,dy; if (top_angle >::iterator m, point_t top_point, point_t left_point, point_t bottom_point,point_t right_point, std::pair &sin_cos) { double s = -sin_cos.first; double c = sin_cos.second; double x1 = top_point.x*c-top_point.y*s; double y1 = top_point.x*s+top_point.y*c; double x2 = left_point.x*c-left_point.y*s; double y2 = left_point.x*s+left_point.y*c; double x3 = bottom_point.x*c-bottom_point.y*s; double y3 = bottom_point.x*s+bottom_point.y*c; double x4 = right_point.x*c-right_point.y*s; double y4 = right_point.x*s+right_point.y*c; double left = std::min(std::min(x1, x2), std::min(x3, x4)); double right = std::max(std::max(x1, x2), std::max(x3, x4)); double top = std::min(std::min(y1, y2), std::min(y3, y4)); double bottom = std::max(std::max(y1, y2), std::max(y3, y4)); int border_count = 0; for (std::vector::iterator p = m->begin(); p != m->end(); p++) { double x = c*(p->x) - s*(p->y); double y = s*(p->x) + c*(p->y); if ((x - left)<2 || (right - x) < 2 || (y - top) < 2 || (bottom - y) < 2) border_count++; } return border_count; } void remove_tables(std::vector > &segments, std::vector > &margins, unsigned int size) { std::vector >::iterator s; std::vector >::iterator m; s = segments.begin(); m = margins.begin(); while (s != segments.end() && m != margins.end()) { if (m->size() <= size) { s++; m++; continue; } int top = INT_MAX, left = INT_MAX, bottom = 0, right = 0; point_t left_point,top_point,right_point,bottom_point; for (std::vector::iterator p = m->begin(); p != m->end(); p++) { if (p->x < left) { left = p->x; left_point = *p; } if (p->x > right) { right = p->x; right_point = *p; } if (p->y < top) { top = p->y; top_point = *p; } if (p->y > bottom) { bottom = p->y; bottom_point = *p; } } double aspect = FLT_MAX; if (right != left) aspect = 1. * (bottom - top) / (right - left); if (aspect >= MAX_ASPECT || aspect <= 1./MAX_ASPECT) { s++; m++; continue; } double area = s->size(); double square_area = (bottom - top+1) * (right - left+1); double ratio = 0; if (square_area != 0) ratio = area / square_area; if (ratio > MAX_RATIO || ratio == 0) { s++; m++; continue; } std::pair zero_angle = std::make_pair(0, 1); int border_count = border_count_in_rotated_frame(m,top_point,left_point,bottom_point,right_point,zero_angle); if (PARTS_IN_MARGIN*border_count > BORDER_COUNT) { s = segments.erase(s); m = margins.erase(m); } else { // perform rotation std::pair sin_cos = find_rotation(top_point,left_point,bottom_point,right_point); int rotated_border_count = border_count_in_rotated_frame(m,top_point,left_point,bottom_point,right_point,sin_cos); if (PARTS_IN_MARGIN*rotated_border_count > BORDER_COUNT && fabs(sin_cos.first)cos(10*PI/180)) { s = segments.erase(s); m = margins.erase(m); } else { s++; m++; } } } } void assemble_clusters( const std::vector > &margins, int dist, const std::vector > &distance_matrix, std::vector &avail, bool text, const std::vector > &area_matrix, std::list > &clusters) { clusters.clear(); std::list bag; for (unsigned int s = 0; s < margins.size(); s++) if (avail[s] == 1) { bag.push_back(s); avail[s] = 2; std::list new_cluster; while (!bag.empty()) { int c = bag.back(); bag.pop_back(); new_cluster.push_back(c); avail[c] = 0; for (unsigned int i = 0; i < margins.size(); i++) if (avail[i] == 1 && distance_matrix[c][i] < dist) // && (!text || area_matrix[i][c] <= 10)) { bag.push_back(i); avail[i] = 2; } } clusters.push_back(new_cluster); } } void remove_text_blocks(const std::list > &clusters, const std::vector > &segments, std::vector &avail) { for (std::list >::const_iterator c = clusters.begin(); c != clusters.end(); c++) { unsigned int area = 0, square_area = 0; double ratio = 0, aspect = 0; int top = INT_MAX, left = INT_MAX, bottom = 0, right = 0; bool fill_below_max = false; for (std::list::const_iterator i = c->begin(); i != c->end(); i++) if (!segments[*i].empty()) { int stop = INT_MAX, sleft = INT_MAX, sbottom = 0, sright = 0; for (std::list::const_iterator p = segments[*i].begin(); p != segments[*i].end(); p++) { if (p->x < sleft) sleft = p->x; if (p->x > sright) sright = p->x; if (p->y < stop) stop = p->y; if (p->y > sbottom) sbottom = p->y; } area = segments[*i].size(); square_area = (sbottom - stop+1) * (sright - sleft+1); if (square_area != 0) ratio = 1. * area / square_area; if (ratio < MAX_RATIO && ratio > 0) fill_below_max = true; if (sleft < left) left = sleft; if (sright > right) right = sright; if (stop < top) top = stop; if (sbottom > bottom) bottom = sbottom; } if (c->size() > TEXT_LINE_SIZE) { if (right != left) aspect = 1. * (bottom - top) / (right - left); if (aspect < MIN_ASPECT || aspect > MAX_ASPECT || !fill_below_max) for (std::list::const_iterator i = c->begin(); i != c->end(); i++) avail[*i] = -1; } } } int locate_first_min(const std::vector &stats) { int peak = 1; for (unsigned int j = 3; j < stats.size(); j++) if (stats[j] > stats[j - 1] && stats[j] > stats[j + 1]) { peak = j; break; } int dist = peak; for (unsigned int j = peak; j < stats.size(); j++) if (stats[j] < stats[j - 1] && stats[j] < stats[j + 1]) { dist = j; break; } return (dist); } int locate_max_entropy(const std::vector > &features, unsigned int max_area_ratio, unsigned int max_dist, std::vector &stats) { std::vector entropy(max_area_ratio, 0); for (unsigned int i = 1; i < max_area_ratio; i++) { int count = 0; for (unsigned int j = 2; j < max_dist; j++) if (features[i][j] == 0) count++; else stats[j]++; if (count > 0) { double probability = 1. * count / (max_dist - 2); entropy[i] -= probability * log(probability); } } int start_b = 1; for (unsigned int i = 2; i < max_area_ratio; i++) { if (entropy[i] > entropy[start_b]) start_b = i; } return (start_b); } bool bulge(const point_t tail, const point_t head, const std::list & seg) { bool r = false; std::vector y(std::max(abs(head.x - tail.x), abs(head.y - tail.y)) + 1, 0); int n=y.size(); if (n<10) return false; bool horizontal = false; if (abs(head.x-tail.x)>abs(head.y-tail.y)) horizontal = true; for (std::list::const_iterator p = seg.begin(); p != seg.end(); p++) { int d; if (horizontal) d=abs(p->x-tail.x); else d=abs(p->y-tail.y); if (dtop) { top = y[i]; pos = i; } /*for (int i=0; i2) flat = false; bool left = true; for (int i=pos-1; i>=midpoint; i--) if (y[i]>y[i+1]+2) left=false; bool right = true; for (int i=pos+1; iy[i-1]+2) right=false; bool peak = true; if (top<1.5*avg || top-avg<2 || n-pos<3 || top-y[n-1]<2 || pos > &margins, std::vector > &segments, std::vector &arrows, std::vector &pluses) { const int len=50; for (int i=0; i hist(len,0); int top_pos=0; int top_value=0; point_t head, tail,center; int min_x, min_y, max_x, max_y; if (segments[i].size()>1000) build_hist(margins[i],hist,len,top_pos,top_value,head,tail,center,min_x, min_y, max_x, max_y); else build_hist(segments[i],hist,len,top_pos,top_value,head,tail,center,min_x, min_y, max_x, max_y); if (top_value>5) { std::vector peaks(1,top_pos); std::vector values(1,top_value); for (int k=1; k=len) pos -= len; int after=pos+1; int before=pos-1; int before2 = pos - 2; if (after>=len) after -=len; if (before<0) before +=len; if (before2<0) before2 +=len; if ( (hist[before]=top_value/2) // find all peaks at least half as high as the top-most { peaks.push_back(pos); values.push_back(hist[pos]); } } if (peaks.size() == 2 && abs(len/2 - abs(peaks[1]-peaks[0]))<=1) // only two peaks are present at 180 degrees { bool ba=bulge(tail,head,segments[i]); bool bb=bulge(head,tail,segments[i]); double l = distance(tail.x, tail.y, head.x, head.y); //std::cout< 2 * MAX_FONT_HEIGHT) { // we found an arrow! //std::cout<0.8 || values[0]-values[1]<=5) && (double(values[2])/values[0]>0.8 || values[0]-values[2]<=5) && (double(values[3])/values[0]>0.8 || values[0]-values[3]<=5)) { bool first=false, second=false, third=false, fourth=false; for (int j=0; j<4; j++) { if (peaks[j] <= 1 || peaks[j] >=len-2) first=true; if (abs(len/4-peaks[j])<=1) second=true; if (abs(len/2-peaks[j])<=1) third=true; if (abs(3*len/4-peaks[j])<=1) fourth=true; } /* for(int k=0; k=len) kk -=len; hist[kk]=0; } bool low=true; /* for(int k=0; k3) low=false; }*/ //std::cout << " " << first <<" "< >::iterator k = margins.begin(); while (k!=margins.end()) { if (k->empty()) k = margins.erase(k); else k++; } std::vector >::iterator l = segments.begin(); while (l!=segments.end()) { if (l->empty()) l = segments.erase(l); else l++; } } bool comp_labels(const label_t &left, const label_t &right) { if (left.x2 < right.x1) return (true); if (std::max(left.y1, left.y2) < std::min(right.y1, right.y2)) return (true); return (false); } int comp_labels_int(const void *l, const void *r) { label_t *left = (label_t *) l; label_t *right = (label_t *) r; if (left->x2 < right->x1) return (-1); if (std::max(left->y1, left->y2) < std::min(right->y1, right->y2)) return (-1); return (1); } std::string ocr_agent_strings(const std::vector > &agents, const Image &image, double threshold, const ColorGray &bgColor, bool verbose) { std::string agent_string; std::vector letters; for (int i=0; i::const_iterator a = agents[i].begin(); a != agents[i].end(); a++) { if (a->xx; if (a->x>right) right=a->x; if (a->yy; if (a->y>bottom) bottom=a->y; } if ((bottom - top) <= 2*MAX_FONT_HEIGHT && (right - left) <= 2*MAX_FONT_WIDTH && (bottom - top) > MIN_FONT_HEIGHT) { char label = 0; label = get_atom_label(image, bgColor, left, top, right, bottom, threshold, (right + left) / 2, top, true, verbose); if (label != 0) { letters_t lt; lt.a=label; lt.x = (left + right) / 2; lt.y = (top + bottom) / 2; lt.r = distance(left, top, right, bottom) / 2; lt.min_x = left; lt.max_x = right; lt.min_y = top; lt.max_y = bottom; lt.free = true; letters.push_back(lt); } } } std::vector label; assemble_labels(letters, letters.size(), label); //sort(label.begin(),label.end(),comp_labels); if (!label.empty()) qsort(&label[0],label.size(),sizeof(label_t),comp_labels_int); for (int i=0; i > &margins, std::vector > &segments, std::vector &arrows, const Image &image, double threshold, const ColorGray &bgColor, bool verbose) { for (int i=0; i > agent_margins; std::vector > agents; double l=distance(arrows[i].tail.x,arrows[i].tail.y,arrows[i].head.x,arrows[i].head.y); bool found=false; for (int j=0; jl) within=false; if (fabs(distance((arrows[i].tail.x+arrows[i].head.x)/2,(arrows[i].tail.y+arrows[i].head.y)/2,margins[j][k].x,margins[j][k].y)) >::iterator k = margins.begin(); while (k!=margins.end()) { if (k->empty()) k = margins.erase(k); else k++; } std::vector >::iterator l = segments.begin(); while (l!=segments.end()) { if (l->empty()) l = segments.erase(l); else l++; } for (int j=0; j &arrows, std::vector &pluses, bool keep, bool verbose, std::list > > &explicit_clusters) { explicit_clusters.clear(); std::vector > segments; std::vector > margins; // 1m34s find_connected_components(image, threshold, bgColor, segments, margins, adaptive); if (verbose) std::cout << "Number of segments: " << segments.size() << '.' << std::endl; if (segments.size() > MAX_SEGMENTS) { segments.clear(); margins.clear(); } if (is_reaction) { find_arrows_pluses(margins,segments,arrows, pluses); find_agent_strings(margins,segments,arrows,image,threshold,bgColor,verbose); } remove_separators(segments, margins, SEPARATOR_ASPECT, SEPARATOR_AREA); remove_tables(segments, margins, SEPARATOR_AREA); // 2m22s std::list > clusters; if (keep) { std::list new_cluster; for (unsigned int s = 0; s < margins.size(); s++) { new_cluster.push_back(s); } clusters.push_back(new_cluster); } else { unsigned int max_dist = MAX_DIST; unsigned int max_area_ratio = MAX_AREA_RATIO; std::vector > distance_matrix(segments.size(), std::vector (segments.size(), INT_MAX)); std::vector > area_matrix(segments.size(), std::vector (segments.size(), INT_MAX)); std::vector > features(max_area_ratio, std::vector (max_dist, 0)); build_distance_matrix(margins, max_dist, distance_matrix, features, segments, max_area_ratio, area_matrix); // 2m53s std::vector avail(margins.size(), 1); /* unsigned int ar; for (unsigned int i = 0; i < margins.size(); i++) for (unsigned int j = i + 1; j < margins.size(); j++) { ar = area_ratio(segments[i].size(), segments[j].size()); if (ar < max_area_ratio && distance_matrix[i][j] < max_dist) features[ar][distance_matrix[i][j]]++; } */ // 5m53s -> new 4m15s std::vector stats(max_dist, 0); int entropy_max = locate_max_entropy(features, max_area_ratio, max_dist, stats); int dist = SINGLE_IMAGE_DIST; if (entropy_max > THRESHOLD_LEVEL && !adaptive && margins.size() > 100) { std::vector text_stats(max_dist, 0); for (unsigned int j = 2; j < max_dist; j++) { text_stats[j] = features[1][j]; //cout << j << " " << text_stats[j] << endl; } int dist_text = locate_first_min(text_stats); std::list > text_blocks; assemble_clusters(margins, dist_text, distance_matrix, avail, true, area_matrix, text_blocks); remove_text_blocks(text_blocks, segments, avail); dist = 2 * dist_text; } // if (dist<20) dist = 20; for (unsigned int i = 0; i < margins.size(); i++) if (avail[i] != -1) avail[i] = 1; assemble_clusters(margins, dist, distance_matrix, avail, false, area_matrix, clusters); } build_explicit_clusters(clusters, segments, explicit_clusters); } void find_box_size(const std::set > &set1, int &x1, int &x2, int &y1, int &y2) { x1 = INT_MAX; y1 = INT_MAX; x2 = 0; y2 = 0; for (std::set >::const_iterator it = set1.begin(); it != set1.end(); ++it) { int px = it->first; int py = it->second; if (px < x1) x1 = px; if (px > x2) x2 = px; if (py < y1) y1 = py; if (py > y2) y2 = py; } } bool check_possible_bracket(std::set > &set1, std::vector > &global_pic, int left, int right, int top, int bottom, int i) { bool res = false; int x1,x2,y1,y2; find_box_size(set1,x1,x2,y1,y2); if (set1.size() <= 10 || (x2 - x1) <= 3 || (y2 - y1) <= 20 ) return false; std::vector > tmp(x2 - x1 + 1, std::vector(y2 - y1 + 1, 0)); int startx = 0; int starty = INT_MAX; for (std::set >::const_iterator it = set1.begin(); it != set1.end(); ++it) { int px = it->first; int py = it->second; tmp[px-x1][py-y1] = 1; if (starty > py - y1) { startx = px - x1; starty = py - y1; } } int middle = (y2 - y1) / 2; std::vector > margin(1, std::make_pair(startx, starty)); std::set > set2; while (!margin.empty()) { startx = margin.back().first; starty = margin.back().second; margin.pop_back(); int reflect = middle + (middle - starty); if (reflect >= 0 && reflect <= y2-y1 && tmp[startx][reflect] != 0) { set2.insert(std::make_pair(startx + x1, starty + y1)); } tmp[startx][starty] = -1; for (int ii = startx-1; ii <= startx+1; ii++) if (ii >= 0 && ii < tmp.size()) for (int j = starty-1; j <= starty+1; j++) if (j >= 0 && j < tmp[ii].size() && tmp[ii][j] == 1) { tmp[ii][j] = 2; margin.push_back(std::make_pair(ii, j)); } } swap(set1,set2); find_box_size(set1,x1,x2,y1,y2); if (set1.size() > 10 && (x2 - x1) > 3 && (y2 - y1) > 20 && (x2 - x1) < (y2 - y1)) { int x = x2 - x1 + 1; int y = y2 - y1 + 1; int f = 1; // if (y > 40) // f = y / 40; x /= f; y /= f; // cout << x1 <<" " << y1 <<" "< >::const_iterator it = set1.begin(); it != set1.end(); ++it) { int px = it->first; int py = it->second; if ((py - y1) / f < y && (px - x1) / f < x && (py - y1) % f == 0 && (px - x1) % f == 0) pic[((py - y1) / f) * x + (px - x1) / f] = 0; } res = detect_bracket(x, y, pic); } return res; } void remove_brackets(int left, int right, int top, int bottom, std::list > >::iterator c, std::set > &brackets) { std::vector > tmp(right - left + 1, std::vector (bottom - top + 1, false)); std::vector > global_pic(right - left + 1, std::vector (bottom - top + 1, false)); for (std::list >::const_iterator s = c->begin(); s != c->end(); s++) for (std::list::const_iterator p = s->begin(); p != s->end(); p++) global_pic[p->x - left][p->y - top] = true; //Image t(Geometry(right + 1, bottom + 1), "white"); for (int i = left + FRAME; i < right - FRAME; i++) { for (std::list >::const_iterator s = c->begin(); s != c->end(); s++) { std::set > set1; for (std::list::const_iterator p = s->begin(); p != s->end(); p++) { if ( i + (i - p->x) < right && i + (i - p->x) - left - 1 >= 0 && p->y - top - 1 >= 0 && i + (i - p->x) - left + 1 < right - left + 1 && p->y - top + 1 < bottom - top + 1 && global_pic[p->x - left][p->y - top] && (global_pic[i + (i - p->x) - left][p->y - top] || global_pic[i + (i - p->x) - left + 1][p->y - top] || global_pic[i + (i - p->x) - left - 1][p->y - top] || global_pic[i + (i - p->x) - left][p->y - top + 1] || global_pic[i + (i - p->x) - left][p->y - top - 1] || global_pic[i + (i - p->x) - left - 1][p->y - top - 1] || global_pic[i + (i - p->x) - left - 1][p->y - top + 1] || global_pic[i + (i - p->x) - left + 1][p->y - top - 1] || global_pic[i + (i - p->x) - left + 1][p->y - top + 1] ) && (p->x < i - 40) ) { set1.insert(std::make_pair(p->x, p->y)); } } if (check_possible_bracket(set1, global_pic, left, right, top, bottom, i)) { for (std::set >::const_iterator it = set1.begin(); it != set1.end(); ++it) { int ox = it->first; int oy = it->second; int px = i + (i - ox); brackets.insert(*it); brackets.insert(std::make_pair(px, oy)); //t.pixelColor(ox, oy, "black"); //t.pixelColor(px, oy, "black"); } } } } //t.write("t.png"); } int prune_clusters(std::list > > &clusters, std::vector &boxes, std::set > &brackets) { int n_boxes = 0; std::list > >::iterator c = clusters.begin(); while (c != clusters.end()) { unsigned int area = 0, square_area = 0; double ratio = 0, aspect = 0; int top = INT_MAX, left = INT_MAX, bottom = 0, right = 0; for (std::list >::const_iterator s = c->begin(); s != c->end(); s++) { for (std::list::const_iterator p = s->begin(); p != s->end(); p++) { if (p->x < left) left = p->x; if (p->x > right) right = p->x; if (p->y < top) top = p->y; if (p->y > bottom) bottom = p->y; } area += s->size(); } if (right != left) aspect = 1. * (bottom - top) / (right - left); square_area = (bottom - top+1) * (right - left+1); if (square_area != 0) ratio = 1. * area / square_area; if (ratio < MAX_RATIO && aspect > MIN_ASPECT && aspect < MAX_ASPECT) { box_t b1; boxes.push_back(b1); boxes[n_boxes].x1 = left; boxes[n_boxes].y1 = top; boxes[n_boxes].x2 = right; boxes[n_boxes].y2 = bottom; remove_brackets(left, right, top, bottom, c, brackets); for (std::list >::const_iterator s = c->begin(); s != c->end(); s++) for (std::list::const_iterator p = s->begin(); p != s->end(); p++) boxes[n_boxes].c.push_back(*p); c++; n_boxes++; } else { c = clusters.erase(c); } } return (n_boxes); } osra-2.1.3/src/osra_ocr_tesseract.cpp0000664000175000017500000000357614115175251016341 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include "config.h" #ifdef HAVE_TESSERACT_LIB #include // NULL #include // free() #include // isalnum() #include // strlen() #include // std::string #include const char UNKNOWN_CHAR = '_'; // Global variable: tesseract::TessBaseAPI tess; void osra_tesseract_init() { tess.Init(NULL, "eng", tesseract::OEM_DEFAULT, NULL, 0, NULL, NULL, false); } void osra_tesseract_destroy() { tess.End(); } char osra_tesseract_ocr(unsigned char *pixmap, int width, int height, const std::string &char_filter) { char result = UNKNOWN_CHAR; char *text = tess.TesseractRect(pixmap, 1, width, 0, 0, width, height); // TODO: Why text length should be exactly 3? Give examples... if (text != NULL && strlen(text) == 3 && isalnum(text[0]) && (char_filter.empty() || char_filter.find(text[0], 0) != std::string::npos)) result = text[0]; free(text); return result; } #endif osra-2.1.3/src/osra_stl.cpp0000664000175000017500000000435314115175251014275 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include "osra_stl.h" // Function: operator<<() // // Helper template method to print various object types. namespace std { std::ostream& operator<<(std::ostream &os, const letters_t &letter) { os << "{letter char:" << letter.a << " x:" << letter.x << " y:" << letter.y << " r:" << letter.r << " free:" << letter.free << '}'; return os; } std::ostream& operator<<(std::ostream &os, const label_t &label) { os << "{label s:" << label.a << " box:" << label.x1 << "x" << label.y1 << "-" << label.x2 << "x" << label.y2 << '}'; return os; } std::ostream& operator<<(std::ostream &os, const atom_t &atom) { os << "{atom label:" << atom.label << " x:" << atom.x << " y:" << atom.y << " n:" << atom.n << " anum:" << atom.anum << '}'; return os; } std::ostream& operator<<(std::ostream &os, const bond_t &bond) { os << "{bond a:" << bond.a << " b:" << bond.b << " type:" << bond.type << " exists:" << bond.exists << " arom:" << bond.arom << " hash:" << bond.hash << " wedge:" << bond.wedge << " up:" << bond.up << " down:" << bond.down<< '}'; return os; } std::ostream& operator<<(std::ostream &os, const fragment_t &fragment) { os << "{fragment " << fragment.x1 << "x" << fragment.y1 << "-" << fragment.x2 << "x" << fragment.y2 << " atoms.size:" << fragment.atom.size() << '}'; return os; } } osra-2.1.3/src/unpaper.h0000664000175000017500000000264114115175251013564 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include // Magick::Image // Header: unpaper.h // // Defines types and functions for unpaper image adjustment module. // // // Section: Functions // // Function: unpaper() // // Performs unpaper image adjustment based on http://unpaper.berlios.de/ // // Parameters: // picture - image object // // Returns: // 0 in case of success or non-zero error code otherwise int unpaper(Magick::Image &picture, double &radians, int &unpaper_dx, int &unpaper_dy); osra-2.1.3/src/osra_fragments.h0000664000175000017500000000611614115175251015125 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_fragments.h // // Declares operations on molecular fragments // #ifndef OSRA_FRAGMENTS_H #define OSRA_FRAGMENTS_H #include "osra.h" //struct: fragment_s // used by to split chemical structure into unconnected molecules. struct fragment_s { //int: x1,y1,x2,y2 //top left and bottom right coordinates of the fragment int x1, y1, x2, y2; //array: atom //vector of atom indices for atoms in a molecule of this fragment std::vector atom; }; //typedef: fragment_t //defines fragment_t type based on fragment_s struct typedef struct fragment_s fragment_t; // // Section: Functions // // Function: find_fragments() // // Find disjointed fragments in a molecule // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // // Returns: // vector of vectors of atom id's which belong to different fragments std::vector > find_fragments(const std::vector &bond, int n_bond, const std::vector &atom); // Function: reconnect_fragments() // // Reconnecting atoms from different fragments if they are less than 1.1 avg bond length apart // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // avg - average bond length // // Returns: // New number of bonds int reconnect_fragments(std::vector &bond, int n_bond, std::vector &atom, double avg); // Function: populate_fragments() // // Transforms vector of vectors of atom ids into a vector of fragments // // Parameters: // frags - vector of vector of atom ids // atom - vector of atoms // // Returns: // vector of fragments std::vector populate_fragments(const std::vector > &frags, const std::vector &atom); // Function: comp_fragments() // // Comparison function used for sorting fragments according to their positions in the picture: top-down, left to right // // Parameters: // aa, bb - two fragments to compare // // Returns: // True if fragment aa is higher or to the left of fragment bb. // False otherwise. bool comp_fragments(const fragment_t &aa, const fragment_t &bb); #endif // OSRA_FRAGMENTS_H osra-2.1.3/src/osra_lib.h0000664000175000017500000000436414115175251013710 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_lib.h // // Defines types and functions of OSRA library. // #include // std::string #include // std:ostream // // Section: Functions // // Function: osra_process_image() // // Parameters: // image_data - the binary image // // Returns: // 0, if processing was completed successfully int osra_process_image( #ifdef OSRA_LIB const char *image_data, int image_length, std::ostream &structure_output_stream, #else const std::string &input_file, const std::string &output_file, #endif int rotate = 0, bool invert = false, int input_resolution = 0, double threshold = 0, int do_unpaper = 0, bool jaggy = false, bool adaptive = false, bool keep_option = false, std::string output_format = "smi", std::string embedded_format = "", bool show_confidence = false, bool show_resolution_guess = false, bool show_page = false, bool show_coordinates = false, bool show_avg_bond_length = false, bool show_learning = false, const std::string &osra_dir = "", const std::string &spelling_file = "", const std::string &superatom_file = "", bool debug = false, bool verbose = false, const std::string &output_image_file_prefix = "", const std::string &resize = "", const std::string &preview = "", const std::string &recognized_chars = "" ); osra-2.1.3/src/greycstoration.h0000664000175000017500000003713714115175251015176 0ustar igorigor/*----------------------------------------------------------------------------- File : greycstoration.h Description : GREYCstoration PLUG-IN allowing easy integration in third parties softwares. (see http://www.greyc.ensicaen.fr/~dtschump/greycstoration/) THIS VERSION IS FOR DEVELOPERS ONLY. IT EASES THE INTEGRATION ALGORITHM IN THIRD PARTIES SOFTWARES. IF YOU ARE A USER OF GREYCSTORATION, PLEASE LOOK AT THE FILE 'greycstoration.cpp' WHICH IS THE SOURCE OF THE COMPLETE COMMAND LINE GREYCSTORATION TOOL. Copyright : David Tschumperle - http://www.greyc.ensicaen.fr/~dtschump/ This software is governed by the CeCILL license under French law and abiding by the rules of distribution of free software. You can use, modify and/ or redistribute the software under the terms of the CeCILL license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info". As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license, users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability. In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software, that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured and, more generally, to use and operate it in the same conditions as regards security. The fact that you are presently reading this means that you have had knowledge of the CeCILL license and that you accept its terms. ------------------------------------------------------------------------------*/ #ifndef cimg_plugin_greycstoration // This tells you about the version of the GREYCstoration algorithm implemented #define cimg_plugin_greycstoration 2.51 //------------------------------------------------------------------------------ // GREYCstoration structure, storing important informations about algorithm // parameters and computing threads //------------------------------------------------------------------------------- struct _greycstoration_params { // Parameters needed for the GREYCstoration regularization algorithm. const CImg *mask; float amplitude, sharpness, anisotropy, alpha, sigma, gfact, dl, da, gauss_prec; unsigned int interpolation; bool fast_approx; // Parameters for the different threads. CImg *source, *temporary; unsigned long *counter; unsigned int tile, tile_border, thread, threads; bool is_running, *stop_request; #if cimg_OS==1 && defined(_PTHREAD_H) pthread_mutex_t *mutex; #elif cimg_OS==2 HANDLE mutex; #else void *mutex; #endif // Default constructor _greycstoration_params():mask(0),amplitude(0),sharpness(0),anisotropy(0),alpha(0),sigma(0),gfact(1), dl(0),da(0),gauss_prec(0),interpolation(0),fast_approx(false), source(0),temporary(0),counter(0),tile(0),tile_border(0),thread(0),threads(0), is_running(false), stop_request(0), mutex(0) {} }; _greycstoration_params greycstoration_params[16]; //------------------------------------------------------------------------------ // GREYCstoration private functions //------------------------------------------------------------------------------- static void greycstoration_mutex_create(_greycstoration_params &p) { if (p.threads>1) { #if cimg_OS==1 && defined(_PTHREAD_H) p.mutex = new pthread_mutex_t; pthread_mutex_init(p.mutex,0); #elif cimg_OS==2 p.mutex = CreateMutex(0,FALSE,0); #endif } } static void greycstoration_mutex_lock(_greycstoration_params &p) { if (p.threads>1) { #if cimg_OS==1 && defined(_PTHREAD_H) if (p.mutex) pthread_mutex_lock(p.mutex); #elif cimg_OS==2 WaitForSingleObject(p.mutex,INFINITE); #endif } } static void greycstoration_mutex_unlock(_greycstoration_params &p) { if (p.threads>1) { #if cimg_OS==1 && defined(_PTHREAD_H) if (p.mutex) pthread_mutex_unlock(p.mutex); #elif cimg_OS==2 ReleaseMutex(p.mutex); #endif } } static void greycstoration_mutex_destroy(_greycstoration_params &p) { if (p.threads>1) { #if cimg_OS==1 && defined(_PTHREAD_H) if (p.mutex) pthread_mutex_destroy(p.mutex); #elif cimg_OS==2 CloseHandle(p.mutex); #endif p.mutex = 0; } } #if cimg_OS==1 || cimg_OS==0 static void* greycstoration_thread(void *arg) { #elif cimg_OS==2 static DWORD WINAPI greycstoration_thread(void *arg) { #endif _greycstoration_params &p = *(_greycstoration_params*)arg; greycstoration_mutex_lock(p); const CImg &mask = *(p.mask); CImg &source = *(p.source); if (!p.tile) { // Non-tiled version //------------------ source.blur_anisotropic(mask,p.amplitude,p.sharpness,p.anisotropy,p.alpha,p.sigma,p.dl,p.da,p.gauss_prec, p.interpolation,p.fast_approx,p.gfact); } else { // Tiled version //--------------- CImg &temporary = *(p.temporary); const bool threed = (source.depth>1); const unsigned int b = p.tile_border; unsigned int ctile = 0; if (threed) { for (unsigned int z=0; z img = source.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true); CImg mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,z-b,xe+b,ye+b,ze+b,true); img.greycstoration_params[0] = p; greycstoration_mutex_unlock(p); img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy, p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact); greycstoration_mutex_lock(p); temporary.draw_image(img.crop(b,b,b,img.width-b,img.height-b,img.depth-b),x,y,z); } } else { for (unsigned int y=0; y img = source.get_crop(x-b,y-b,xe+b,ye+b,true); CImg mask_tile = mask.is_empty()?mask:mask.get_crop(x-b,y-b,xe+b,ye+b,true); img.greycstoration_params[0] = p; greycstoration_mutex_unlock(p); img.blur_anisotropic(mask_tile,p.amplitude,p.sharpness,p.anisotropy, p.alpha,p.sigma,p.dl,p.da,p.gauss_prec,p.interpolation,p.fast_approx,p.gfact); temporary.draw_image(img.crop(b,b,img.width-b,img.height-b),x,y); greycstoration_mutex_lock(p); } } } greycstoration_mutex_unlock(p); if (!p.thread) { if (p.threads>1) { bool stopflag = true; do { stopflag = true; for (unsigned int k=1; kis_running; } //! Force GREYCstoration threads to stop. CImg& greycstoration_stop() { if (greycstoration_is_running()) { *(greycstoration_params->stop_request) = true; while (greycstoration_params->is_running) cimg::wait(50); } return *this; } //! Return the GREYCstoration progression indice. float greycstoration_progress() const { if (!greycstoration_is_running()) return 0.0f; const unsigned long counter = greycstoration_params->counter?*(greycstoration_params->counter):0; const float da = greycstoration_params->da; float maxcounter = 0; if (greycstoration_params->tile==0) maxcounter = width*height*(1 + 360/da); else { const unsigned int t = greycstoration_params->tile, b = greycstoration_params->tile_border, n = (1+(width-1)/t)*(1+(height-1)/t)*(1+(depth-1)/t); maxcounter = (width*height + n*4*b*(b + t))*(1 + 360/da); } return cimg::min(counter*99.9f/maxcounter,99.9f); } //! Run the threaded GREYCstoration algorithm on the instance image, using a mask. CImg& greycstoration_run(const CImg& mask, const float amplitude=60, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int threads=1) { if (greycstoration_is_running()) throw CImgInstanceException("CImg::greycstoration_run() : A GREYCstoration thread is already running on" " the instance image (%u,%u,%u,%u,%p).",width,height,depth,dim,data); else { if (!mask.is_empty() && !mask.is_sameXY(*this)) throw CImgArgumentException("CImg<%s>::greycstoration_run() : Given mask (%u,%u,%u,%u,%p) and instance image " "(%u,%u,%u,%u,%p) have different dimensions.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data,width,height,depth,dim,data); if (threads>16) cimg::warn("CImg<%s>::greycstoration_run() : Multi-threading mode limited to 16 threads max."); const unsigned int ntile = (tile && (tile1 && tile *const temporary = ntile?new CImg(*this):0; unsigned long *const counter = new unsigned long; *counter = 0; bool *const stop_request = new bool; *stop_request = false; for (unsigned int k=0; k<(nthreads?nthreads:1); k++) { greycstoration_params[k].mask = &mask; greycstoration_params[k].amplitude = amplitude; greycstoration_params[k].sharpness = sharpness; greycstoration_params[k].anisotropy = anisotropy; greycstoration_params[k].alpha = alpha; greycstoration_params[k].sigma = sigma; greycstoration_params[k].gfact = gfact; greycstoration_params[k].dl = dl; greycstoration_params[k].da = da; greycstoration_params[k].gauss_prec = gauss_prec; greycstoration_params[k].interpolation = interpolation; greycstoration_params[k].fast_approx = fast_approx; greycstoration_params[k].source = this; greycstoration_params[k].temporary = temporary; greycstoration_params[k].counter = counter; greycstoration_params[k].tile = ntile; greycstoration_params[k].tile_border = tile_border; greycstoration_params[k].thread = k; greycstoration_params[k].threads = nthreads; greycstoration_params[k].is_running = true; greycstoration_params[k].stop_request = stop_request; if (k) greycstoration_params[k].mutex = greycstoration_params[0].mutex; else greycstoration_mutex_create(greycstoration_params[0]); } if (nthreads) // Threaded version { #if cimg_OS==1 #ifdef _PTHREAD_H pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); for (unsigned int k=0; kthreads; k++) { pthread_t thread; const int err = pthread_create(&thread, &attr, greycstoration_thread, (void*)(greycstoration_params+k)); if (err) throw CImgException("CImg<%s>::greycstoration_run() : pthread_create returned error %d", pixel_type(), err); } #endif #elif cimg_OS==2 for (unsigned int k=0; kthreads; k++) { unsigned long ThreadID = 0; CreateThread(0,0,greycstoration_thread,(void*)(greycstoration_params+k),0,&ThreadID); } #else throw CImgInstanceException("CImg::greycstoration_run() : Threads are not supported, please define cimg_OS first."); #endif } else greycstoration_thread((void*)greycstoration_params); // Non-threaded version } return *this; } //! Run the GREYCstoration algorithm on the instance image. CImg& greycstoration_run(const float amplitude=50, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float gfact=1.0f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const unsigned int tile=0, const unsigned int tile_border=0, const unsigned int threads=1) { static const CImg empty_mask; return greycstoration_run(empty_mask,amplitude,sharpness,anisotropy,alpha,sigma,gfact,dl,da,gauss_prec, interpolation,fast_approx,tile,tile_border,threads); } #endif osra-2.1.3/src/Makefile0000664000175000017500000001014414115175251013376 0ustar igorigor# # This makefile is responsible for building the executable. # include ../Makefile.inc include Makefile.dep .PHONY: clean_obj LIB_VERSION := $(LIB_MAJOR_VERSION).$(LIB_MINOR_VERSION).$(LIB_PATCH_VERSION) TARGETS := osra$(EXEEXT) OBJ_LIB := osra_lib.o osra_grayscale.o osra_fragments.o osra_segment.o osra_labels.o osra_thin.o osra_common.o osra_stl.o osra_structure.o osra_anisotropic.o osra_ocr.o osra_openbabel.o mcdlutil.o unpaper.o osra_reaction.o ifdef TESSERACT_LIB OBJ_LIB += osra_ocr_tesseract.o endif OBJ_CLI := $(OBJ_LIB) osra.o ifdef OSRA_LIB TARGETS += libosra.a libosra$(SHAREDEXT) endif ifdef OSRA_JAVA OBJ_JAVA := $(OBJ_LIB) osra_java.o TARGETS += libosra_java$(SHAREDEXT) endif ifdef OSRA_ANDROID OBJ_ANDROID := $(OBJ_LIB) osra.o endif all: # From here: http://stackoverflow.com/questions/5584872/complex-conditions-check-in-makefile/5586785#5586785 ifneq ($(or $(OSRA_LIB),$(OSRA_JAVA),$(OSRA_ANDROID)),) $(RM) -f $(OBJ_CLI) endif $(MAKE) osra$(EXEEXT) ifdef OSRA_LIB $(RM) -f $(OBJ_LIB) $(MAKE) libosra.a $(MAKE) libosra$(SHAREDEXT) endif ifdef OSRA_JAVA $(RM) -f $(OBJ_JAVA) $(MAKE) libosra_java$(SHAREDEXT) endif ifdef OSRA_ANDROID $(RM) -f $(OBJ_ANDROID) $(MAKE) libosra_andriod$(SHAREDEXT) endif # We have to update the timestaps of the targets, otherwise "install" target will try to re-link and will cause missed symbols: touch $(TARGETS) osra$(EXEEXT): $(OBJ_CLI) $(LINK.cpp) -o $@ $(OBJ_CLI) $(LIBS) ifdef OSRA_LIB libosra.a: CXXFLAGS += -fPIC -DOSRA_LIB libosra.a: $(OBJ_LIB) $(AR) cru $@ $(OBJ_LIB) $(RANLIB) $@ libosra$(SHAREDEXT): CXXFLAGS += -fPIC -DOSRA_LIB libosra$(SHAREDEXT): $(OBJ_LIB) $(LINK.cpp) $(LDSHAREDFLAGS) -o $@ $(OBJ_LIB) $(LIBS) endif ifdef OSRA_JAVA libosra_java$(SHAREDEXT): CXXFLAGS += -fPIC -DOSRA_LIB -DOSRA_JAVA libosra_java$(SHAREDEXT): $(OBJ_JAVA) $(LINK.cpp) $(LDSHAREDFLAGS) -o $@ $(OBJ_JAVA) $(LIBS) endif ifdef OSRA_ANDROID libosra_andriod$(SHAREDEXT): CXXFLAGS += -fPIC -DOSRA_LIB -DOSRA_ANDROID libosra_andriod$(SHAREDEXT): $(OBJ_ANDROID) $(LINK.cpp) $(LDSHAREDFLAGS) -o $@ $(OBJ_ANDROID) $(LIBS) endif Makefile.dep: ../Makefile.inc $(wildcard *.cpp) $(CXX) $(CPPFLAGS) -MM $^ > Makefile.dep # Correct installation for Cygwin/MinGW also needs handling of libosra.dll.a, which is not done here: install: $(TARGETS) $(INSTALL_DIR) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) osra$(EXEEXT) $(DESTDIR)$(bindir) ifdef OSRA_LIB $(INSTALL_DIR) $(DESTDIR)$(libdir) $(DESTDIR)$(includedir) $(DESTDIR)$(libdir)/pkgconfig $(INSTALL_PROGRAM) libosra$(SHAREDEXT) $(DESTDIR)$(libdir)/libosra$(SHAREDEXT).$(LIB_VERSION) $(LN_S) -f libosra$(SHAREDEXT).$(LIB_VERSION) $(DESTDIR)$(libdir)/libosra$(SHAREDEXT).$(LIB_MAJOR_VERSION) $(LN_S) -f libosra$(SHAREDEXT).$(LIB_MAJOR_VERSION) $(DESTDIR)$(libdir)/libosra$(SHAREDEXT) $(INSTALL_DATA) libosra.a $(DESTDIR)$(libdir) $(INSTALL_DATA) osra_lib.h $(DESTDIR)$(includedir) $(INSTALL_DATA) ../package/linux/osra.pc $(DESTDIR)$(libdir)/pkgconfig endif ifdef OSRA_JAVA $(INSTALL_DIR) $(DESTDIR)$(libdir) $(INSTALL_PROGRAM) libosra_java$(SHAREDEXT) $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT).$(LIB_VERSION) $(LN_S) -f libosra_java$(SHAREDEXT).$(LIB_VERSION) $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT).$(LIB_MAJOR_VERSION) $(LN_S) -f libosra_java$(SHAREDEXT).$(LIB_MAJOR_VERSION) $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT) endif # "install" and "make" tools auomatically autodetect the extension for executables, but "rm" needs extension correction. uninstall: -$(RM) -f \ $(DESTDIR)$(bindir)/osra$(EXEEXT) \ $(DESTDIR)$(libdir)/libosra$(SHAREDEXT).$(LIB_VERSION) \ $(DESTDIR)$(libdir)/libosra$(SHAREDEXT).$(LIB_MAJOR_VERSION) \ $(DESTDIR)$(libdir)/libosra$(SHAREDEXT) \ $(DESTDIR)$(libdir)/libosra.a \ $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT).$(LIB_VERSION) \ $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT).$(LIB_MAJOR_VERSION) \ $(DESTDIR)$(libdir)/libosra_java$(SHAREDEXT) $(DESTDIR)$(includedir)/osra_lib.h \ $(DESTDIR)$(libdir)/pkgconfig/osra.pc clean: -$(RM) -f *.o osra$(EXEEXT) libosra*.* distclean: clean -$(RM) -f config.h Makefile.dep ../Makefile.inc: ../Makefile.inc.in ../config.status cd .. && ./config.status osra-2.1.3/src/osra_anisotropic.cpp0000664000175000017500000001101314115175251016014 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #define cimg_use_magick #define cimg_plugin "greycstoration.h" #include "CImg.h" using namespace cimg_library; using namespace Magick; Image anisotropic_smoothing(const Image &image, int width, int height, const float amplitude, const float sharpness, const float anisotropy, const float alpha, const float sigma) { Image res(Geometry(width, height), "white"); res.type(GrayscaleType); #pragma omp critical { CImg source(width, height, 1, 1, 0); unsigned char color[1] = { 0 }; unsigned char cc; ColorGray c; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) { c = image.pixelColor(i, j); color[0] = (unsigned char) (255 * c.shade()); source.draw_point(i, j, color); } CImg dest(source); const float gfact = 1.; //const float amplitude = 5.; // 20 // const float sharpness = 0.3; //const float anisotropy = 1.; //const float alpha = .2; //0.6 //const float sigma = 1.1; // 2. const float dl = 0.8; const float da = 30.; const float gauss_prec = 2.; const unsigned int interp = 0; const bool fast_approx = true; const unsigned int tile = 512; const unsigned int btile = 4; const unsigned int threads = 1; // orig - 2 dest.greycstoration_run(amplitude, sharpness, anisotropy, alpha, sigma, gfact, dl, da, gauss_prec, interp, fast_approx, tile, btile, threads); do { cimg::wait(1); } while (dest.greycstoration_is_running()); for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) { cc = dest(i, j); c.shade(1. * cc / 255); res.pixelColor(i, j, c); } } return (res); } Image anisotropic_scaling(const Image &image, int width, int height, int nw, int nh) { Image res(Geometry(nw, nh), "white"); res.type(GrayscaleType); #pragma omp critical { CImg source(width, height, 1, 1, 0); unsigned char color[1] = { 0 }; unsigned char cc; ColorGray c; for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) { c = image.pixelColor(i, j); color[0] = (unsigned char) (255 * c.shade()); source.draw_point(i, j, color); } //const float gfact = (sizeof(T) == 2) ? 1.0f / 256 : 1.0f; const float gfact = 1.; const float amplitude = 20.; // 40 20! const float sharpness = 0.2; // 0.2! 0.3 const float anisotropy = 1.; const float alpha = .6; //0.6! 0.8 const float sigma = 2.; //1.1 2.! const float dl = 0.8; const float da = 30.; const float gauss_prec = 2.; const unsigned int interp = 0; const bool fast_approx = true; const unsigned int tile = 512; // 512 0 const unsigned int btile = 4; const unsigned int threads = 1; // 2 1 const unsigned int init = 5; CImg mask; mask.assign(source.dimx(), source.dimy(), 1, 1, 255); mask = !mask.resize(nw, nh, 1, 1, 4); source.resize(nw, nh, 1, -100, init); CImg dest(source); dest.greycstoration_run(mask, amplitude, sharpness, anisotropy, alpha, sigma, gfact, dl, da, gauss_prec, interp, fast_approx, tile, btile, threads); do { cimg::wait(1); } while (dest.greycstoration_is_running()); for (int i = 0; i < nw; i++) for (int j = 0; j < nh; j++) { cc = dest(i, j); c.shade(1. * cc / 255); res.pixelColor(i, j, c); } } return (res); } osra-2.1.3/src/osra_java.cpp0000664000175000017500000001073614115175251014416 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include "config.h" // PACKAGE_VERSION #ifdef OSRA_JAVA /* Fix for jlong definition in jni.h on some versions of gcc on Windows */ #if defined(__GNUC__) && !defined(__INTEL_COMPILER) typedef long long __int64; #endif #include #include // calloc(), free() #include // std::string #include // std:ostream #include // std:ostringstream #include "osra_lib.h" extern "C" { /* * Class: net_sf_osra_OsraLib * Method: processImage * Signature: ([BLjava/io/Writer;Ljava/lang/String;Ljava/lang/String;ZZZ)I */ JNIEXPORT jint JNICALL Java_net_sf_osra_OsraLib_processImage(JNIEnv *, jclass, jbyteArray, jobject, jint, jboolean,jint,jdouble,jint, jboolean, jboolean,jstring, jstring, jboolean, jboolean,jboolean, jboolean, jboolean); /* * Class: net_sf_osra_OsraLib * Method: getVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_net_sf_osra_OsraLib_getVersion(JNIEnv *, jclass); } JNIEXPORT jint JNICALL Java_net_sf_osra_OsraLib_processImage(JNIEnv *j_env, jclass j_class, jbyteArray j_image_data, jobject j_writer, jint j_rotate, jboolean j_invert, jint j_input_resolution, jdouble j_threshold, jint j_do_unpaper, jboolean j_jaggy, jboolean j_adaptive_option, jstring j_output_format, jstring j_embedded_format, jboolean j_output_confidence, jboolean j_show_resolution_guess, jboolean j_show_page, jboolean j_output_coordinates, jboolean j_output_avg_bond_length) { const char *output_format = j_env->GetStringUTFChars(j_output_format, NULL); const char *embedded_format = j_env->GetStringUTFChars(j_embedded_format, NULL); const char *image_data = (char *) j_env->GetByteArrayElements(j_image_data, NULL); int result = -1; if (image_data != NULL) { // Perhaps there is a more optimal way to bridge from std:ostream to java.io.Writer. // See http://stackoverflow.com/questions/524524/creating-an-ostream/524590#524590 std::ostringstream structure_output_stream; result = osra_process_image( image_data, j_env->GetArrayLength(j_image_data), structure_output_stream, j_rotate, j_invert, j_input_resolution, j_threshold, j_do_unpaper, j_jaggy, j_adaptive_option, output_format, embedded_format, j_output_confidence, j_show_resolution_guess, j_show_page, j_output_coordinates, j_output_avg_bond_length, "." ); j_env->ReleaseByteArrayElements(j_image_data, (jbyte *) image_data, JNI_ABORT); // Locate java.io.Writer#write(String) method: jclass j_writer_class = j_env->FindClass("java/io/Writer"); jmethodID write_method_id = j_env->GetMethodID(j_writer_class, "write", "(Ljava/lang/String;)V"); jstring j_string = j_env->NewStringUTF(structure_output_stream.str().c_str()); j_env->CallVoidMethod(j_writer, write_method_id, j_string); j_env->DeleteLocalRef(j_writer_class); j_env->DeleteLocalRef(j_string); } j_env->ReleaseStringUTFChars(j_output_format, output_format); j_env->ReleaseStringUTFChars(j_embedded_format, embedded_format); return result; } JNIEXPORT jstring JNICALL Java_net_sf_osra_OsraLib_getVersion(JNIEnv *j_env, jclass j_class) { return j_env->NewStringUTF(PACKAGE_VERSION); } #endif osra-2.1.3/src/osra_lib.cpp0000664000175000017500000013575214115175251014251 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include // fclose #include // malloc(), free() #include // fabs(double) #include // FLT_MAX #include // INT_MAX #include // sdt::list #include // std::vector #include #include // std::sort, std::min(double, double), std::max(double, double) #include // std::ostream, std::cout #include // std::ofstream, std::ifstream #include // std:ostringstream #include extern "C" { #include #include } #include #include #include #include "osra.h" #include "osra_grayscale.h" #include "osra_segment.h" #include "osra_fragments.h" #include "osra_labels.h" #include "osra_thin.h" #include "osra_common.h" #include "osra_structure.h" #include "osra_lib.h" #include "osra_ocr.h" #include "osra_openbabel.h" #include "osra_reaction.h" #include "osra_anisotropic.h" #include "osra_stl.h" #include "unpaper.h" #include "config.h" // DATA_DIR using namespace Magick; void set_select_resolution(std::vector &select_resolution, int input_resolution) { if (input_resolution == 0) { select_resolution[0] = 72; select_resolution[1] = 150; select_resolution[2] = 300; select_resolution[3] = 300; // No thinning select_resolution[4] = 500; } } double set_threshold(double threshold,int resolution) { double THRESHOLD_BOND = threshold; if (THRESHOLD_BOND < 0.0001) { if (resolution >= 150) { THRESHOLD_BOND = THRESHOLD_GLOBAL; } else { THRESHOLD_BOND = THRESHOLD_LOW_RES; } } return THRESHOLD_BOND; } int load_superatom_spelling_maps( std::map &spelling, std::map &superatom, const std::string &osra_dir, const std::string &spelling_file, const std::string &superatom_file, bool verbose) { // Loading the program data files into maps: if (!((spelling_file.length() != 0 && load_config_map(spelling_file, spelling)) || load_config_map(std::string(DATA_DIR) + "/" + SPELLING_TXT, spelling) || load_config_map(osra_dir + "/" + SPELLING_TXT, spelling))) { std::cerr << "Cannot open " << SPELLING_TXT << " file (tried locations \"" << DATA_DIR << "\", \"" << osra_dir << "\"). Specify the custom file location via -l option." << std::endl; return ERROR_SPELLING_FILE_IS_MISSING; } if (!((superatom_file.length() != 0 && load_config_map(superatom_file, superatom)) || load_config_map(std::string(DATA_DIR) + "/" + SUPERATOM_TXT, superatom) || load_config_map(osra_dir + "/" + SUPERATOM_TXT, superatom))) { std::cerr << "Cannot open " << SUPERATOM_TXT << " file (tried locations \"" << DATA_DIR << "\", \"" << osra_dir << "\"). Specify the custom file location via -a option." << std::endl; return ERROR_SUPERATOM_FILE_IS_MISSING; } if (verbose) std::cout << "spelling (size: " << spelling.size() << ") and superatom (size: " << superatom.size() << ") dictionaries are loaded." << std::endl; return 0; } void create_thick_box(Image &orig_box,Image &thick_box,int &width,int &height,int &resolution,int &working_resolution,double &box_scale, ColorGray bgColor, double THRESHOLD_BOND, int res_iter, bool &thick, bool jaggy) { if (resolution >= 300) { int max_hist; double nf45; double nf = noise_factor(orig_box, width, height, bgColor, THRESHOLD_BOND, resolution, max_hist, nf45); //if (max_hist < 5) thick = false; if (res_iter == NUM_RESOLUTIONS-2) // no thinning { working_resolution = 300; thick_box = orig_box; width = thick_box.columns(); height = thick_box.rows(); thick = false; //nf = noise_factor(orig_box, width, height, bgColor, THRESHOLD_BOND, resolution, max_hist, nf45); } if (res_iter == NUM_RESOLUTIONS-1) { if (max_hist >= 6) { int new_resolution = max_hist * 300 / 4; int percent = (100 * 300) / new_resolution; resolution = new_resolution; std::ostringstream scale; scale << percent << "%"; orig_box.scale(scale.str()); box_scale /= (double) percent/100; working_resolution = 300; thick_box = orig_box; width = thick_box.columns(); height = thick_box.rows(); nf = noise_factor(orig_box, width, height, bgColor, THRESHOLD_BOND, resolution, max_hist, nf45); } else { resolution = 500; int percent = (100 * 300) / resolution; std::ostringstream scale; scale << percent << "%"; orig_box.scale(scale.str()); box_scale /= (double) percent/100; working_resolution = 300; thick_box = orig_box; width = thick_box.columns(); height = thick_box.rows(); thick = false; nf = noise_factor(orig_box, width, height, bgColor, THRESHOLD_BOND, resolution, max_hist, nf45); } } if (jaggy) { orig_box.scale("50%"); box_scale *= 2; working_resolution = 150; //orig_box.scale("33%"); //box_scale *= 3; // working_resolution = 100; thick_box = orig_box; width = thick_box.columns(); height = thick_box.rows(); } else if (nf > 0.5 && nf < 1. && max_hist <= 6)// && res_iter != 3 && max_hist <= 6) try { thick_box = anisotropic_smoothing(orig_box, width, height, 20, 0.3, 1.0, 0.6, 2); } catch (...) { thick_box = orig_box; } /*else if (nf45 > 0.9 && nf45 < 1.2 && max_hist == 3) { //orig_box = anisotropic_smoothing(thick_box, width, height, 60, 0.3, 0.6, 4., 2.); orig_box.scale("50%"); thick_box = orig_box; //working_resolution = 150; width = thick_box.columns(); height = thick_box.rows(); //thick = false; }*/ else thick_box = orig_box; } else if (resolution < 300 && resolution > 150) { int nw = width * 300 / resolution; int nh = height * 300 / resolution; thick_box = anisotropic_scaling(orig_box, width, height, nw, nh); width = thick_box.columns(); height = thick_box.rows(); int percent = (100 * 300) / resolution; std::ostringstream scale; scale << percent << "%"; orig_box.scale(scale.str()); box_scale /= (double) percent/100; working_resolution = 300; } else thick_box = orig_box; } potrace_state_t * const raster_to_vector(Image &box,ColorGray bgColor, double THRESHOLD_BOND,int width,int height,int working_resolution) { potrace_param_t * const param = potrace_param_default(); param->alphamax = 5e-324; // this has been changed in potrace-1.11 //param->turnpolicy = POTRACE_TURNPOLICY_MINORITY; param->turdsize = 0; param->turnpolicy = POTRACE_TURNPOLICY_MINORITY; double c_width = 1. * width * 72 / working_resolution; double c_height = 1. * height * 72 / working_resolution; if (c_height * c_width < SMALL_PICTURE_AREA) param->turnpolicy = POTRACE_TURNPOLICY_BLACK; potrace_bitmap_t * const bm = bm_new(width, height); for (int i = 0; i < width; i++) for (int j = 0; j < height; j++) BM_PUT(bm, i, j, get_pixel(box, bgColor, i, j, THRESHOLD_BOND)); potrace_state_t * const st = potrace_trace(param, bm); if (bm != NULL) { free(bm->map); free(bm); } potrace_param_free(param); return(st); } void rotate_point(int &x, int &y, int midX, int midY, double rotation) { double dX = x - midX; double dY = y - midY; dY = -dY; double sinval = sin(-rotation); double cosval = cos(-rotation); double diffX = dX * cosval + dY * sinval; double diffY = dX * (-sinval) + dY * cosval; diffY = -diffY; if ((rotation > 1*PI/4 && rotation < 3*PI/4) || (rotation > 5*PI/4 && rotation < 7*PI/4)) std::swap(midX, midY); x = midX + int(diffX); y = midY + int(diffY); } void rotate_coordinate_box(box_t &coordinate_box,double rotation,int width,int height) { if (rotation == 0) return; int midX = width/2; int midY = height/2; int x1 = coordinate_box.x1; int y1 = coordinate_box.y1; int x4 = coordinate_box.x2; int y4 = coordinate_box.y2; int x2 = x1; int y2 = y4; int x3 = x4; int y3 = y1; rotate_point(x1,y1,midX,midY,rotation); rotate_point(x2,y2,midX,midY,rotation); rotate_point(x3,y3,midX,midY,rotation); rotate_point(x4,y4,midX,midY,rotation); if ((rotation > 1*PI/4 && rotation < 3*PI/4) || (rotation > 5*PI/4 && rotation < 7*PI/4)) std::swap(width, height); coordinate_box.x1 = std::max(0, std::min(std::min(std::min(x1,x2), x3), x4)); coordinate_box.x2 = std::min(width, std::max(std::max(std::max(x1,x2), x3), x4)); coordinate_box.y1 = std::max(0, std::min(std::min(std::min(y1,y2), y3), y4)); coordinate_box.y2 = std::min(height, std::max(std::max(std::max(y1,y2), y3), y4)); } void split_fragments_and_assemble_structure_record( std::vector &atom, int n_atom, std::vector &bond, int n_bond, const std::vector &boxes, int l, int k, int resolution, int res_iter, const std::string &output_image_file_prefix, Image &image, Image &orig_box, int real_font_width, int real_font_height, double thickness, double avg_bond_length, const std::map &superatom, int real_atoms, int real_bonds, int bond_max_type, double box_scale, double page_scale, double rotation, int unpaper_dx, int unpaper_dy, std::string output_format, const std::string &embedded_format, bool is_reaction, bool show_confidence, bool show_resolution_guess, bool show_page, bool show_coordinates, bool show_avg_bond_length, std::vector > &array_of_structures, std::vector > &array_of_avg_bonds, std::vector > &array_of_ind_conf, std::vector > &array_of_images, std::vector > &array_of_boxes, int &total_boxes, double &total_confidence, int n_letters, bool show_learning, int resolution_iteration, bool verbose, const std::vector& brackets) { std::vector frag_atom; std::vector frag_bond; if (real_atoms > MIN_A_COUNT && real_atoms < MAX_A_COUNT && real_bonds < MAX_B_COUNT && bond_max_type>0 && bond_max_type<4) { int num_frag; num_frag = resolve_bridge_bonds(atom, n_atom, bond, n_bond, 2 * thickness, avg_bond_length, superatom, verbose); collapse_bonds(atom, bond, n_bond, avg_bond_length / 4); collapse_atoms(atom, bond, n_atom, n_bond, 3); remove_zero_bonds(bond, n_bond, atom); extend_terminal_bond_to_bonds(atom, bond, n_bond, avg_bond_length, 7, 0); remove_small_terminal_bonds(bond, n_bond, atom, avg_bond_length); n_bond = reconnect_fragments(bond, n_bond, atom, avg_bond_length); collapse_atoms(atom, bond, n_atom, n_bond, 1); mark_terminal_atoms(bond, n_bond, atom, n_atom); const std::vector > &frags = find_fragments(bond, n_bond, atom); std::vector fragments = populate_fragments(frags, atom); std::sort(fragments.begin(), fragments.end(), comp_fragments); for (unsigned int i = 0; i < fragments.size(); i++) { if (verbose) std::cout << "Considering fragment #" << i + 1 << " " << fragments[i].x1 << "x" << fragments[i].y1 << "-" << fragments[i].x2 << "x" << fragments[i].y2 << ", atoms: " << fragments[i].atom.size() << '.' << std::endl; if (verbose) { std::cout << "Page scale: " << page_scale << std::endl; std::cout << "Unpaper dx, dy: " << unpaper_dx<<" "< MIN_A_COUNT) { frag_atom.clear(); for (int a = 0; a < n_atom; a++) { frag_atom.push_back(atom[a]); frag_atom[a].exists = false; } for (unsigned int j = 0; j < fragments[i].atom.size(); j++) frag_atom[fragments[i].atom[j]].exists = atom[fragments[i].atom[j]].exists; frag_bond.clear(); for (int b = 0; b < n_bond; b++) { frag_bond.push_back(bond[b]); } remove_zero_bonds(frag_bond, n_bond, frag_atom); double confidence = 0; molecule_statistics_t molecule_statistics; int page_number = l + 1; box_t coordinate_box,rel_box; if (fragments.size()>1) { coordinate_box.x1 = (int) (-(double)page_scale * unpaper_dx + (double) page_scale * boxes[k].x1 + (double) page_scale * box_scale * fragments[i].x1 - (double) page_scale * FRAME); coordinate_box.y1 = (int) (-(double)page_scale * unpaper_dy + (double) page_scale * boxes[k].y1 + (double) page_scale * box_scale * fragments[i].y1 - (double) page_scale * FRAME); coordinate_box.x2 = (int) (-(double)page_scale * unpaper_dx + (double) page_scale * boxes[k].x1 + (double) page_scale * box_scale * fragments[i].x2 - (double) page_scale * FRAME); coordinate_box.y2 = (int) (-(double)page_scale * unpaper_dy + (double) page_scale * boxes[k].y1 + (double) page_scale * box_scale * fragments[i].y2 - (double) page_scale * FRAME); rotate_coordinate_box(coordinate_box,rotation,image.columns(),image.rows()); rel_box.x1 = (int)((double)boxes[k].x1 + (double) box_scale * fragments[i].x1 - FRAME); rel_box.y1 = (int)((double)boxes[k].y1 + (double) box_scale * fragments[i].y1 - FRAME); rel_box.x2 = (int)((double)boxes[k].x1 + (double) box_scale * fragments[i].x2 - FRAME); rel_box.y2 = (int)((double)boxes[k].y1 + (double) box_scale * fragments[i].y2 - FRAME); } else { coordinate_box.x1 = (int) (-(double)page_scale * unpaper_dx + (double) page_scale * boxes[k].x1); coordinate_box.y1 = (int) (-(double)page_scale * unpaper_dy + (double) page_scale * boxes[k].y1); coordinate_box.x2 = (int) (-(double)page_scale * unpaper_dx + (double) page_scale * boxes[k].x2); coordinate_box.y2 = (int) (-(double)page_scale * unpaper_dy + (double) page_scale * boxes[k].y2); rotate_coordinate_box(coordinate_box,rotation,image.columns(),image.rows()); rel_box.x1 = boxes[k].x1; rel_box.y1 = boxes[k].y1; rel_box.x2 = boxes[k].x2; rel_box.y2 = boxes[k].y2; } if (verbose) std::cout << "Coordinate box: " << coordinate_box.x1 << "x" << coordinate_box.y1 << "-" << coordinate_box.x2 << "x" << coordinate_box.y2 << "." << std::endl; if (is_reaction) output_format = SUBSTITUTE_REACTION_FORMAT; std::string structure = get_formatted_structure(frag_atom, frag_bond, n_bond, output_format, embedded_format, molecule_statistics, confidence, show_confidence, avg_bond_length, page_scale * box_scale * avg_bond_length, show_avg_bond_length, show_resolution_guess ? &resolution : NULL, show_page ? &page_number : NULL, show_coordinates ? &coordinate_box : NULL, superatom, n_letters, show_learning, resolution_iteration, verbose, brackets); if (molecule_statistics.fragments > 0 && molecule_statistics.fragments < MAX_FRAGMENTS && molecule_statistics.num_atoms>MIN_A_COUNT && molecule_statistics.num_bonds>0 ) { if ((molecule_statistics.rings56 > 0 || molecule_statistics.num_organic_non_carbon_atoms > 0) && molecule_statistics.num_bonds>MIN_B_COUNT && molecule_statistics.num_small_angles < 3 && avg_bond_length > real_font_height ) { array_of_structures[res_iter].push_back(structure); array_of_avg_bonds[res_iter].push_back(page_scale * box_scale * avg_bond_length); array_of_ind_conf[res_iter].push_back(confidence); array_of_boxes[res_iter].push_back(rel_box); if (!output_image_file_prefix.empty()) { Image tmp = image; if (!is_reaction) { Geometry geometry = (fragments.size() > 1) ? Geometry(box_scale * fragments[i].x2 - box_scale * fragments[i].x1, // box_scale * fragments[i].y2 - box_scale * fragments[i].y1, // boxes[k].x1 + box_scale * fragments[i].x1 - FRAME , // boxes[k].y1 + box_scale * fragments[i].y1 - FRAME ) : Geometry(boxes[k].x2 - boxes[k].x1, boxes[k].y2 - boxes[k].y1, boxes[k].x1, boxes[k].y1); try { tmp.crop(geometry); } catch (...) { tmp = orig_box; } } array_of_images[res_iter].push_back(tmp); } total_boxes++; total_confidence += confidence; if (verbose) std::cout << "Result: " << res_iter << " " << structure << " " << confidence << std::endl; } } } } } } int count_recognized_chars(std::vector &atom, std::vector& bond) { std::string char_filter = "oOcCNHsSBMeEXYZRPp23456789AF"; std::set atoms; for (int i=0; i::iterator a = atoms.begin(); a != atoms.end(); a++) for (int i = 0; i < atom[*a].label.size(); i++) { if (char_filter.find(atom[*a].label[i]) != std::string::npos) r++; } return r; } Image process_pdf_page(poppler::document* doc, poppler::page_renderer &r, int l, int resolution) { poppler::page* p = doc->create_page(l); poppler::image im = r.render_page(p, resolution, resolution); Image image(Geometry(im.width(), im.height()), "white"); image.modifyImage(); image.type(TrueColorType); const char *d = im.const_data(); int bytes_per_row = im.bytes_per_row(); int bytes_per_pixel = bytes_per_row / im.width(); for (int row = 0; row < im.height(); ++row) { for (int col = 0; col < im.width(); ++col) { int offset = row * bytes_per_row + col * bytes_per_pixel; unsigned char r = -1, g = -1, b = -1, a = -1; switch(im.format()) { case poppler::image::format_mono : r = g = b = *reinterpret_cast(d + offset); break; case poppler::image::format_gray8 : r = g = b = *reinterpret_cast(d + offset); break; case poppler::image::format_rgb24 : r = *reinterpret_cast(d + offset); g = *reinterpret_cast(d + offset + 1); b = *reinterpret_cast(d + offset + 2); break; case poppler::image::format_argb32 : const unsigned int pixel = *reinterpret_cast(d + offset); r = (pixel >> 16) & 0xff; g = (pixel >> 8) & 0xff; b = pixel & 0xff; break; } if (r >= 0 && g >= 0 && b >= 0) image.pixelColor(col, row, ColorRGB(double(r) / 255, double(g) / 255, double(b) / 255)); } } return image; } extern job_t *OCR_JOB; extern job_t *JOB; #ifdef OSRA_LIB int global_init_state; #endif // Function: osra_init() // // Initialises OSRA library. Should be called at e.g. program startup. This function is automatically called for both SO library and CLI utility. // See this section for details about library init/cleanup: http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html#INIT-AND-CLEANUP // Below attribute marker is GNU compiler specific. void __attribute__ ((constructor)) osra_init() { // Necessary for GraphicsMagick-1.3.8 according to http://www.graphicsmagick.org/1.3/NEWS.html#january-21-2010: MagickLib::InitializeMagick(NULL); osra_ocr_init(); #ifdef OSRA_LIB global_init_state = osra_openbabel_init(); if (global_init_state != 0) std::cerr << "OpenBabel initialization failure." << std::endl; #endif srand(1); } // Function: osra_destroy() // // Releases all resources allocated by OSRA library. Should be called at e.g. program exit. This function is automatically called for both SO library and CLI utility. // See this section for details about library init/cleanup: http://www.faqs.org/docs/Linux-HOWTO/Program-Library-HOWTO.html#INIT-AND-CLEANUP // Below attribute marker is GNU compiler specific. void __attribute__ ((destructor)) osra_destroy() { #ifdef OSRA_LIB MagickLib::DestroyMagick(); #endif osra_ocr_destroy(); } int osra_process_image( #ifdef OSRA_LIB const char *image_data, int image_length, std::ostream &structure_output_stream, #else const std::string &input_file, const std::string &output_file, #endif int rotate, bool invert, int input_resolution, double threshold, int do_unpaper, bool jaggy, bool adaptive_option, bool keep_option, std::string output_format, std::string embedded_format, bool show_confidence, bool show_resolution_guess, bool show_page, bool show_coordinates, bool show_avg_bond_length, bool show_learning, const std::string &osra_dir, const std::string &spelling_file, const std::string &superatom_file, bool debug, bool verbose, const std::string &output_image_file_prefix, const std::string &resize, const std::string &preview, const std::string &recognized_chars ) { #ifdef OSRA_LIB if (global_init_state != 0) return global_init_state; #endif std::transform(output_format.begin(), output_format.end(), output_format.begin(), ::tolower); std::transform(embedded_format.begin(), embedded_format.end(), embedded_format.begin(), ::tolower); std::map spelling, superatom; int err = load_superatom_spelling_maps(spelling, superatom, osra_dir, spelling_file, superatom_file, verbose); if (err != 0) return err; std::string type; #ifdef OSRA_LIB Blob blob(image_data, image_length); #endif try { Image image_typer; #ifdef OSRA_LIB image_typer.ping(blob); #else image_typer.ping(input_file); #endif type = image_typer.magick(); } catch (...) { // Unfortunately, GraphicsMagick does not throw exceptions in all cases, so it behaves inconsistent, see // https://sourceforge.net/tracker/?func=detail&aid=3022955&group_id=40728&atid=428740 } //int stderr_copy = dup(2); //fclose(stderr); int page = 1; poppler::document* poppler_doc = NULL; if (type.empty() || type == "PDF" || type == "PS") { #ifdef OSRA_LIB poppler_doc = poppler::document::load_from_raw_data(image_data, image_length); #else poppler_doc = poppler::document::load_from_file(input_file); #endif } if (poppler_doc) { page = poppler_doc->pages(); type = "PDF"; } else if (type == "PDF" || type == "PS") { type.clear(); } else if (!type.empty() && type != "PDF") { #ifdef OSRA_LIB page = count_pages(blob); #else page = count_pages(input_file); #endif } // dup2(stderr_copy, 2); //close(stderr_copy); if (type.empty()) { #ifdef OSRA_LIB std::cerr << "Cannot detect blob image type" << std::endl; #else std::cerr << "Cannot open file \"" << input_file << '"' << std::endl; #endif return ERROR_UNKNOWN_IMAGE_TYPE; } if (verbose) std::cout << "Image type: " << type << '.' << std::endl; #ifndef OSRA_LIB std::ofstream outfile; if (!output_file.empty()) { outfile.open(output_file.c_str(), std::ios::out | std::ios::trunc); if (outfile.bad() || !outfile.is_open()) { std::cerr << "Cannot open file \"" << output_file << "\" for output" << std::endl; return ERROR_OUTPUT_FILE_OPEN_FAILED; } } #endif if (!embedded_format.empty() && !(output_format == "sdf" && (embedded_format == "inchi" || embedded_format == "smi" || embedded_format == "can"))) { std::cerr << "Embedded format option is only possible if output format is SDF and option can have only inchi, smi, or can values." << std::endl; return ERROR_ILLEGAL_ARGUMENT_COMBINATION; } // This will hide the output "Warning: non-positive median line gap" from GOCR. Remove after this is fixed: fclose(stderr); OpenBabel::obErrorLog.StopLogging(); bool is_reaction = false; if (output_format == "cmlr" || output_format == "rsmi" || output_format =="rxn") is_reaction = true; std::vector > pages_of_structures(page, std::vector (0)); std::vector > pages_of_images(page, std::vector (0)); std::vector > pages_of_avg_bonds(page, std::vector (0)); std::vector > pages_of_ind_conf(page, std::vector (0)); std::vector > pages_of_boxes(page, std::vector (0)); std::vector > arrows(page, std::vector(0)); std::vector > pluses(page, std::vector(0)); int total_structure_count = 0; int num_resolutions = NUM_RESOLUTIONS; if (input_resolution != 0) num_resolutions = 1; std::vector array_of_confidence(num_resolutions, 0); std::vector boxes_per_res(num_resolutions,0); std::vector select_resolution(num_resolutions, input_resolution); set_select_resolution(select_resolution,input_resolution); std::vector > > array_of_structures_page( page, std::vector >(num_resolutions)); std::vector > > array_of_avg_bonds_page( page, std::vector >(num_resolutions)); std::vector > > array_of_ind_conf_page( page, std::vector >(num_resolutions)); std::vector > > array_of_images_page( page, std::vector > (num_resolutions)); std::vector > > array_of_boxes_page( page, std::vector >(num_resolutions)); //#pragma omp parallel for default(shared) private(OCR_JOB,JOB) for (int l = 0; l < page; l++) { Image image; double page_scale=1; poppler::page_renderer poppler_renderer; poppler_renderer.set_image_format(poppler::image::format_enum::format_gray8); //poppler_renderer.set_line_mode(poppler::page_renderer::line_mode_enum::line_solid); //poppler_renderer.set_render_hint(poppler::page_renderer::render_hint::antialiasing); //poppler_renderer.set_render_hint(poppler::page_renderer::render_hint::text_antialiasing); //poppler_renderer.set_render_hint(poppler::page_renderer::render_hint::text_hinting); int ttt = 0; if (verbose) std::cout << "Processing page " << (l+1) << " out of " << page << "..." << std::endl; if ((type == "PDF" || type == "PS")) // https://stackoverflow.com/questions/10601995/how-to-display-a-pdf-in-its-true-scale-with-poppler { if (input_resolution != 0) page_scale *= (double) 110 / input_resolution; else page_scale *= (double) 110 / 300; } if (poppler_doc) // process PDF and PS files { int resolution = input_resolution; if (resolution == 0) resolution = 300; image = process_pdf_page(poppler_doc, poppler_renderer, l, resolution); } else { #ifdef OSRA_LIB image.read(blob); #else std::ostringstream pname; pname << input_file << "[" << l << "]"; #pragma omp critical { image.read(pname.str()); } #endif } if (l == 0 && !preview.empty()) { try { image.write(preview); } catch(...) {} } image.modifyImage(); bool adaptive = convert_to_gray(image, invert, adaptive_option, verbose); std::vector > array_of_structures(num_resolutions); std::vector > array_of_avg_bonds(num_resolutions), array_of_ind_conf(num_resolutions); std::vector > array_of_images(num_resolutions); std::vector > array_of_boxes(num_resolutions); if (input_resolution > 300) { int percent = (100 * 300) / input_resolution; std::ostringstream scale; scale << percent << "%"; image.scale(scale.str()); page_scale /= (double) percent / 100; } if (verbose) std::cout << "Input resolutions are " << select_resolution << std::endl; ColorGray bgColor = getBgColor(image); if (rotate != 0) { image.backgroundColor(bgColor); image.rotate(rotate); } double rotation = rotate * PI / 180; int unpaper_dx = 0; int unpaper_dy = 0; for (int i = 0; i < do_unpaper; i++) { double radians=0; int dx=0, dy=0; unpaper(image,radians,dx,dy); rotation +=radians; unpaper_dx +=dx; unpaper_dy +=dy; } // 0.1 is used for THRESHOLD_BOND here to allow for farther processing. std::list > > clusters; find_segments(image, 0.1, bgColor, adaptive, is_reaction, arrows[l], pluses[l], keep_option, verbose, clusters); if (verbose) std::cout << "Number of clusters: " << clusters.size() << '.' << std::endl; std::vector boxes; std::set > brackets; int n_boxes = prune_clusters(clusters, boxes, brackets); std::sort(boxes.begin(), boxes.end(), comp_boxes); if (verbose) std::cout << "Number of boxes: " << boxes.size() << '.' << std::endl; for (int res_iter = 0; res_iter < num_resolutions; res_iter++) { int total_boxes = 0; double total_confidence = 0; int resolution = select_resolution[res_iter]; int working_resolution = resolution; if (resolution > 300) working_resolution = 300; double THRESHOLD_BOND = set_threshold(threshold,resolution); int max_font_height = MAX_FONT_HEIGHT * working_resolution / 150; int max_font_width = MAX_FONT_WIDTH * working_resolution / 150; bool thick = true; if (resolution < 150) thick = false; else if (resolution == 150 && !jaggy) thick = false; //Image dbg = image; //dbg.modifyImage(); //dbg.backgroundColor("white"); //dbg.erase(); //dbg.type(TrueColorType); for (int k = 0; k < n_boxes; k++) if ((boxes[k].x2 - boxes[k].x1) > max_font_width && (boxes[k].y2 - boxes[k].y1) > max_font_height && !boxes[k].c.empty() && ((boxes[k].x2 - boxes[k].x1) > 2 * max_font_width || (boxes[k].y2 - boxes[k].y1) > 2 * max_font_height)) { int n_atom = 0, n_bond = 0, n_letters = 0, n_label = 0; std::vector atom; std::vector bond; std::vector letters; std::vector label; double box_scale = 1; Image orig_box(Geometry(boxes[k].x2 - boxes[k].x1 + 2 * FRAME, boxes[k].y2 - boxes[k].y1 + 2 * FRAME), bgColor); for (unsigned int p = 0; p < boxes[k].c.size(); p++) { int x = boxes[k].c[p].x; int y = boxes[k].c[p].y; ColorGray color = image.pixelColor(x, y); //dbg.pixelColor(x, y, color); orig_box.pixelColor(x - boxes[k].x1 + FRAME, y - boxes[k].y1 + FRAME, color); } int width = orig_box.columns(); int height = orig_box.rows(); Image thick_box; create_thick_box(orig_box,thick_box,width,height,resolution,working_resolution,box_scale,bgColor,THRESHOLD_BOND,res_iter,thick,jaggy); if (verbose) std::cout << "Analysing box " << boxes[k].x1 << "x" << boxes[k].y1 << "-" << boxes[k].x2 << "x" << boxes[k].y2 << " using working resolution " << working_resolution << '.' << std::endl; Image box; if (thick) box = thin_image(thick_box, THRESHOLD_BOND, bgColor); else box = thick_box; potrace_state_t * const st = raster_to_vector(box,bgColor,THRESHOLD_BOND,width,height,working_resolution); potrace_path_t const * const p = st->plist; n_atom = find_atoms(p, atom, bond, &n_bond,width,height); if (n_atom >= MAX_ATOMS) { if (st != NULL) potrace_state_free(st); continue; } int real_font_width, real_font_height; n_letters = find_chars(p, orig_box, letters, atom, bond, n_atom, n_bond, height, width, bgColor, THRESHOLD_BOND, max_font_width, max_font_height, real_font_width, real_font_height,verbose, recognized_chars); if (verbose) std::cout << "Number of atoms: " << n_atom << ", bonds: " << n_bond << ", " << n_letters << " letters: " << n_letters << " " << letters << " after find_atoms()" << std::endl; double avg_bond_length = percentile75(bond, n_bond, atom); double max_area = avg_bond_length * 5; if (thick) max_area = avg_bond_length; n_letters = find_plus_minus(p, orig_box, bgColor, THRESHOLD_BOND, letters, atom, bond, n_atom, n_bond, height, width, real_font_height, real_font_width, n_letters, avg_bond_length); n_atom = find_small_bonds(p, atom, bond, n_atom, &n_bond, max_area, avg_bond_length / 2, 5); //remove_small_bonds_in_chars(atom,bond,letters); find_old_aromatic_bonds(p, bond, n_bond, atom, n_atom, avg_bond_length); if (verbose) std::cout << "Number of atoms: " << n_atom << ", bonds: " << n_bond << ", " << n_letters << "letters: " << letters << " after find_old_aromatic_bonds()" << std::endl; double dist = 3.; if (working_resolution < 150) dist = 2; double thickness = skeletize(atom, bond, n_bond, box, THRESHOLD_BOND, bgColor, dist, avg_bond_length); remove_disconnected_atoms(atom, bond, n_atom, n_bond); collapse_atoms(atom, bond, n_atom, n_bond, 3); remove_zero_bonds(bond, n_bond, atom); n_bond = find_wavy_bonds(bond,n_bond,atom,avg_bond_length); n_letters = find_fused_chars(bond, n_bond, atom, letters, n_letters, real_font_height, real_font_width, 0, orig_box, bgColor, THRESHOLD_BOND, 3, verbose, recognized_chars); n_letters = find_fused_chars(bond, n_bond, atom, letters, n_letters, real_font_height, real_font_width, '*', orig_box, bgColor, THRESHOLD_BOND, 5, verbose, recognized_chars); flatten_bonds(bond, n_bond, atom, 3); remove_zero_bonds(bond, n_bond, atom); avg_bond_length = percentile75(bond, n_bond, atom); if (verbose) std::cout << "Average bond length: " << avg_bond_length << std::endl; double max_dist_double_bond = dist_double_bonds(atom, bond, n_bond, avg_bond_length); n_bond = double_triple_bonds(atom, bond, n_bond, avg_bond_length, n_atom, max_dist_double_bond); n_atom = find_dashed_bonds(p, atom, bond, n_atom, &n_bond, std::max(MAX_DASH, int(avg_bond_length / 3)), avg_bond_length, orig_box, bgColor, THRESHOLD_BOND, thick, avg_bond_length, letters); n_letters = remove_small_bonds(bond, n_bond, atom, letters, n_letters, real_font_height, MIN_FONT_HEIGHT, avg_bond_length); n_letters = find_numbers(p, orig_box, letters, atom, bond, n_atom, n_bond, height, width, bgColor, THRESHOLD_BOND, n_letters); dist = 4.; if (working_resolution < 300) dist = 3; if (working_resolution < 150) dist = 2; n_bond = fix_one_sided_bonds(bond, n_bond, atom, dist, avg_bond_length); n_letters = clean_unrecognized_characters(bond, n_bond, atom, real_font_height, real_font_width, 4, letters, n_letters); thickness = find_wedge_bonds(thick_box, atom, n_atom, bond, n_bond, bgColor, THRESHOLD_BOND, max_dist_double_bond, avg_bond_length, 3, 1); n_label = assemble_labels(letters, n_letters, label); if (verbose) std::cout << n_label << " labels: " << label << " after assemble_labels()" << std::endl; remove_disconnected_atoms(atom, bond, n_atom, n_bond); collapse_atoms(atom, bond, n_atom, n_bond, thickness); remove_zero_bonds(bond, n_bond, atom); flatten_bonds(bond, n_bond, atom, 2 * thickness); remove_zero_bonds(bond, n_bond, atom); avg_bond_length = percentile75(bond, n_bond, atom); collapse_double_bonds(bond, n_bond, atom, max_dist_double_bond); extend_terminal_bond_to_label(atom, letters, n_letters, bond, n_bond, label, n_label, avg_bond_length / 2, thickness, max_dist_double_bond); remove_disconnected_atoms(atom, bond, n_atom, n_bond); collapse_atoms(atom, bond, n_atom, n_bond, thickness); collapse_doubleup_bonds(bond, n_bond); remove_zero_bonds(bond, n_bond, atom); flatten_bonds(bond, n_bond, atom, thickness); remove_zero_bonds(bond, n_bond, atom); remove_disconnected_atoms(atom, bond, n_atom, n_bond); extend_terminal_bond_to_bonds(atom, bond, n_bond, avg_bond_length, 2 * thickness, max_dist_double_bond); std::vector bracket_boxes; remove_bracket_atoms(atom, n_atom, bond, n_bond, brackets, thickness, boxes[k].x1, boxes[k].y1, box_scale, real_font_width, real_font_height, bracket_boxes); remove_zero_bonds(bond, n_bond, atom); remove_vertical_bonds_close_to_brackets(bracket_boxes, atom, bond, n_bond, thickness, avg_bond_length); remove_zero_bonds(bond, n_bond, atom); flatten_bonds(bond, n_bond, atom, 2*thickness); assign_labels_to_brackets(bracket_boxes, label, n_label, letters, n_letters, real_font_width, real_font_height); collapse_atoms(atom, bond, n_atom, n_bond, 3); remove_zero_bonds(bond, n_bond, atom); flatten_bonds(bond, n_bond, atom, 5); remove_zero_bonds(bond, n_bond, atom); n_letters = clean_unrecognized_characters(bond, n_bond, atom, real_font_height, real_font_width, 0, letters, n_letters); int num_recognized_chars = count_recognized_chars(atom,bond); assign_charge(atom, bond, n_atom, n_bond, spelling, superatom, debug); find_up_down_bonds(bond, n_bond, atom, thickness); //remove_high_order_bonds_connected_to_hash_bonds(bond, n_bond, atom, avg_bond_length); int real_atoms = count_atoms(atom, n_atom); int bond_max_type = 0; int real_bonds = count_bonds(bond, n_bond,bond_max_type); if (verbose) std::cout << "Final number of atoms: " << real_atoms << ", bonds: " << real_bonds << ", chars: " << n_letters << '.' << std::endl; //if (ttt++ == 0) debug_image(orig_box, atom, n_atom, bond, n_bond, "tmp.png"); split_fragments_and_assemble_structure_record(atom,n_atom,bond,n_bond,boxes, l,k,resolution,res_iter,output_image_file_prefix,image,orig_box,real_font_width,real_font_height, thickness,avg_bond_length,superatom,real_atoms,real_bonds,bond_max_type, box_scale,page_scale,rotation,unpaper_dx,unpaper_dy,output_format,embedded_format,is_reaction,show_confidence, show_resolution_guess,show_page,show_coordinates, show_avg_bond_length,array_of_structures, array_of_avg_bonds,array_of_ind_conf,array_of_images,array_of_boxes,total_boxes,total_confidence, num_recognized_chars,show_learning,res_iter,verbose, bracket_boxes); if (st != NULL) potrace_state_free(st); } array_of_confidence[res_iter] += total_confidence; boxes_per_res[res_iter] += total_boxes; //dbg.write("debug.png"); } #pragma omp critical { if (show_learning) for (int j = 0; j < num_resolutions; j++) for (unsigned int i = 0; i < array_of_structures[j].size(); i++) { pages_of_structures[l].push_back(array_of_structures[j][i]); if (!output_image_file_prefix.empty()) pages_of_images[l].push_back(array_of_images[j][i]); pages_of_avg_bonds[l].push_back(array_of_avg_bonds[j][i]); pages_of_ind_conf[l].push_back(array_of_ind_conf[j][i]); pages_of_boxes[l].push_back(array_of_boxes[j][i]); total_structure_count++; } else for (int j = 0; j < num_resolutions; j++) { array_of_structures_page[l][j] = array_of_structures[j]; if (!output_image_file_prefix.empty()) array_of_images_page[l][j] = array_of_images[j]; array_of_avg_bonds_page[l][j] = array_of_avg_bonds[j]; array_of_ind_conf_page[l][j] = array_of_ind_conf[j]; array_of_boxes_page[l][j] = array_of_boxes[j]; } } } double max_conf = -FLT_MAX; int max_res = 0; for (int i = 0; i < num_resolutions; i++) { if (boxes_per_res[i] > 0 && array_of_confidence[i]/boxes_per_res[i] > max_conf) { max_conf = array_of_confidence[i]/boxes_per_res[i]; max_res = i; } } for (int i = 0; i < num_resolutions; i++) if (boxes_per_res[i] > 0 && array_of_confidence[i]/boxes_per_res[i] == max_conf && select_resolution[i] == 300) // second 300 dpi is without thinning { max_res = i; break; } if (!show_learning) for (int l = 0; l < page; l++) { pages_of_structures[l] = array_of_structures_page[l][max_res]; if (!output_image_file_prefix.empty()) pages_of_images[l] = array_of_images_page[l][max_res]; pages_of_avg_bonds[l] = array_of_avg_bonds_page[l][max_res]; pages_of_ind_conf[l] = array_of_ind_conf_page[l][max_res]; pages_of_boxes[l] = array_of_boxes_page[l][max_res]; total_structure_count += array_of_structures_page[l][max_res].size(); } double best_bond = 0; //if (total_structure_count >= STRUCTURE_COUNT) // find_limits_on_avg_bond(best_bond, pages_of_avg_bonds, pages_of_ind_conf); // If multiple pages are processed at several resolutions different pages // may be processed at different resolutions leading to a seemingly different average bond length // Currently multi-page documents (PDF and PS) are all processed at the same resolution // and single-page images have all structures on the page at the same resolution //cout << min_bond << " " << max_bond << endl; #ifdef OSRA_LIB std::ostream &out_stream = structure_output_stream; #else std::ostream &out_stream = outfile.is_open() ? outfile : std::cout; #endif // For Andriod version we will find the structure with maximum confidence value, as the common usecase for Andriod is to analyse the // image (taken by embedded photo camera) that usually contains just one molecule: double max_confidence = -FLT_MAX; int l_index = 0; int i_index = 0; int image_count = 0; for (int l = 0; l < page; l++) { for (unsigned int i = 0; i < pages_of_structures[l].size(); i++) if (best_bond == 0 || (pages_of_avg_bonds[l][i] > best_bond/2 && pages_of_avg_bonds[l][i] < 2*best_bond)) { if (pages_of_ind_conf[l][i] > max_confidence) { max_confidence = pages_of_ind_conf[l][i]; l_index = l; i_index = i; } if (output_format != "mol" && !is_reaction) { out_stream << pages_of_structures[l][i]; // Dump this structure into a separate file: if (!output_image_file_prefix.empty()) { std::ostringstream fname; fname << output_image_file_prefix << image_count << ".png"; image_count++; if (fname.str() != "") { Image tmp = pages_of_images[l][i]; if (resize != "") { tmp.scale(resize); } tmp.write(fname.str()); } } } } if (is_reaction && !arrows[l].empty()) { std::vector reactions; std::vector rbox; arrange_reactions(arrows[l], pages_of_boxes[l], pluses[l], reactions, rbox, pages_of_structures[l],output_format); for (int k=0; k #include #include #include #include #include void collect_inchi(std::set &inchi1, const std::string &name1, int &count) { count = 0; OpenBabel::OBConversion obconversion; obconversion.SetInFormat("sdf"); obconversion.SetOutFormat("inchi"); obconversion.SetOptions("K", obconversion.OUTOPTIONS); OpenBabel::OBMol mol; bool notatend = obconversion.ReadFile(&mol, name1); while (notatend) { std::string inchi = obconversion.WriteString(&mol); if (!inchi.empty()) inchi1.insert(inchi); count++; mol.Clear(); notatend = obconversion.Read(&mol); } } template size_t size_intersection (InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) { size_t result = 0; while (first1!=last1 && first2!=last2) { if (*first1<*first2) ++first1; else if (*first2<*first1) ++first2; else { ++result; ++first1; ++first2; } } return result; } int main(int argc,char **argv) { if(argc<3) { std::cerr << "Usage: " << argv[0] <<" ground_truth/ computed/" << std::endl; return 1; } OpenBabel::obErrorLog.StopLogging(); std::string folder1(argv[1]); std::string folder2(argv[2]); DIR *dir; struct dirent *ent; size_t total = 0, identical = 0, computed = 0; if ((dir = opendir (folder1.c_str())) != NULL) { while ((ent = readdir (dir)) != NULL) { std::string name1 = folder1 + ent->d_name; std::string name2 = folder2 + ent->d_name; if (name1.size() > 4 && name1.substr(name1.size()-4) == ".sdf") { std::set inchi1,inchi2; int count1, count2; collect_inchi(inchi1,name1, count1); collect_inchi(inchi2,name2, count2); total += inchi1.size(); computed += count2 - (count1 - inchi1.size()); identical += size_intersection(inchi1.begin(), inchi1.end(), inchi2.begin(), inchi2.end()); } } closedir (dir); } else { std::cerr << "Unable to open directory " << argv[1] << std::endl; return 1; } std::cout << total <<" "<< identical << " " << double(identical) / total << " " << double(identical) / computed << std::endl; return(0); } osra-2.1.3/src/osra_reaction.h0000664000175000017500000000401214115175251014734 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_reaction.h // // Defines functions dealing with generating a reaction type output // #define SUBSTITUTE_REACTION_FORMAT "mol" // // Section: Functions // // Function: arrange_reactions // // Create a reaction representation for input vector of structures // // Parameters: // arrows - a vector of arrow_t objects representing arrows found during segmentation // page_of_boxes - a vector of box_t objects representing bounding boxes of molecules // pluses - a vector of plus sing centers // results - a vector of strings to represent output results // page_of_structures - input vector of reactants, intermediates and products // output_format - format of the returned result, i.e. rsmi or cmlr // void arrange_reactions(std::vector &arrows, const std::vector &page_of_boxes, const std::vector &pluses, std::vector &results, std::vector &rbox, const std::vector &page_of_structures, const std::string &output_format); osra-2.1.3/src/osra.cpp0000664000175000017500000001573314115175251013417 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include // strncpy() #include // dirname() #include #include "osra_lib.h" #include "config.h" // PACKAGE_VERSION int main(int argc, char **argv ) { TCLAP::CmdLine cmd("OSRA: Optical Structure Recognition Application, created by Igor Filippov, 2013", ' ', PACKAGE_VERSION); // // Image pre-processing options // TCLAP::ValueArg rotate_option("R", "rotate", "Rotate image clockwise by specified number of degrees", false, 0, "0..360"); cmd.add(rotate_option); TCLAP::SwitchArg invert_option("n", "negate", "Invert color (white on black)", false); cmd.add(invert_option); TCLAP::ValueArg resolution_option("r", "resolution", "Resolution in dots per inch", false, 0, "default: auto"); cmd.add(resolution_option); TCLAP::ValueArg threshold_option("t", "threshold", "Gray level threshold", false, 0, "0.2..0.8"); cmd.add(threshold_option); TCLAP::ValueArg do_unpaper_option("u", "unpaper", "Pre-process image with unpaper algorithm, rounds", false, 0, "default: 0 rounds"); cmd.add(do_unpaper_option); TCLAP::SwitchArg jaggy_option("j", "jaggy", "Additional thinning/scaling down of low quality documents", false); cmd.add(jaggy_option); TCLAP::SwitchArg adaptive_option("i", "adaptive", "Adaptive thresholding pre-processing, useful for low light/low contrast images", false); cmd.add(adaptive_option); TCLAP::SwitchArg keep_option("k", "keep", "Keep image unsegmented, do not separate molecules from text", false); cmd.add(keep_option); // // Output format options // TCLAP::ValueArg output_format_option("f", "format", "Output format", false, "can", "can/smi/sdf"); cmd.add(output_format_option); TCLAP::ValueArg embedded_format_option("", "embedded-format", "Embedded format", false, "", "inchi/smi/can"); cmd.add(embedded_format_option); TCLAP::SwitchArg show_confidence_option("p", "print", "Print out confidence estimate", false); cmd.add(show_confidence_option); TCLAP::SwitchArg show_resolution_guess_option("g", "guess", "Print out resolution guess", false); cmd.add(show_resolution_guess_option); TCLAP::SwitchArg show_page_option("e", "page", "Show page number for PDF/PS/TIFF documents (only for SDF/SMI/CAN output format)", false); cmd.add(show_page_option); TCLAP::SwitchArg show_coordinates_option("c", "coordinates", "Show surrounding box coordinates (only for SDF/SMI/CAN output format)", false); cmd.add(show_coordinates_option); TCLAP::SwitchArg show_avg_bond_length_option("b", "bond", "Show average bond length in pixels (only for SDF/SMI/CAN output format)", false); cmd.add(show_avg_bond_length_option); // // Dictionaries options // TCLAP::ValueArg spelling_file_option("l", "spelling", "Spelling correction dictionary", false, "", "configfile"); cmd.add(spelling_file_option); TCLAP::ValueArg superatom_file_option("a", "superatom", "Superatom label map to SMILES", false, "", "configfile"); cmd.add(superatom_file_option); TCLAP::ValueArg recognized_chars("", "ocr", "OCR character filter", false, "", "oOcCnNHFsSBuUgMeEXYZRPp23456789AmThDGQ"); cmd.add(recognized_chars); // // Debugging options // TCLAP::SwitchArg debug_option("d", "debug", "Print out debug information on spelling corrections", false); cmd.add(debug_option); TCLAP::SwitchArg verbose_option("v", "verbose", "Be verbose and print the program flow", false); cmd.add(verbose_option); TCLAP::ValueArg output_image_file_prefix_option("o", "output", "Write recognized structures to image files with given prefix", false, "", "filename prefix"); cmd.add(output_image_file_prefix_option); TCLAP::ValueArg resize_option("s", "size", "Resize image on output", false, "", "dimensions, 300x400"); cmd.add(resize_option); TCLAP::ValueArg preview_option("", "preview", "Preview Image", false, "", "filename"); cmd.add(preview_option); // // Input-output options // TCLAP::UnlabeledValueArg input_file_option("in", "input file", true, "", "filename"); cmd.add(input_file_option); TCLAP::ValueArg output_file_option("w", "write", "Write recognized structures to text file", false, "", "filename"); cmd.add(output_file_option); TCLAP::SwitchArg show_learning_option("", "learn", "Print out all structure guesses with confidence parameters", false); cmd.add(show_learning_option); cmd.parse(argc, argv); // Calculating the current dir: char progname[1024]; strncpy(progname, cmd.getProgramName().c_str(), sizeof(progname) - 1); progname[sizeof(progname) - 1] = '\0'; std::string osra_dir = dirname(progname); int result = osra_process_image( input_file_option.getValue(), output_file_option.getValue(), rotate_option.getValue(), invert_option.getValue(), resolution_option.getValue(), threshold_option.getValue(), do_unpaper_option.getValue(), jaggy_option.getValue(), adaptive_option.getValue(), keep_option.getValue(), output_format_option.getValue(), embedded_format_option.getValue(), show_confidence_option.getValue(), show_resolution_guess_option.getValue(), show_page_option.getValue(), show_coordinates_option.getValue(), show_avg_bond_length_option.getValue(), show_learning_option.getValue(), osra_dir, spelling_file_option.getValue(), superatom_file_option.getValue(), debug_option.getValue(), verbose_option.getValue(), output_image_file_prefix_option.getValue(), resize_option.getValue(), preview_option.getValue(), recognized_chars.getValue() ); return result; } osra-2.1.3/src/detect.cpp0000664000175000017500000000437314115175251013721 0ustar igorigor// g++ -I/usr/local/include/openbabel-2.0/ -static detect.cpp -o recall -L/usr/local/lib -lopenbabel -lz -linchi #include #include #include #include #include #include void collect_inchi(std::set &inchi1, const std::string &name1) { OpenBabel::OBConversion obconversion; obconversion.SetInFormat("sdf"); obconversion.SetOutFormat("inchi"); obconversion.SetOptions("K", obconversion.OUTOPTIONS); OpenBabel::OBMol mol; bool notatend = obconversion.ReadFile(&mol, name1); while (notatend) { std::string inchi = obconversion.WriteString(&mol); if (!inchi.empty()) inchi1.insert(inchi); mol.Clear(); notatend = obconversion.Read(&mol); } } void print_errors(const std::set &inchi1, const std::string &name2) { OpenBabel::OBConversion obconversion; obconversion.SetInFormat("sdf"); obconversion.SetOutFormat("inchi"); obconversion.SetOptions("K", obconversion.OUTOPTIONS); OpenBabel::OBMol mol; bool notatend = obconversion.ReadFile(&mol, name2); int i = 0; while (notatend) { std::string inchi = obconversion.WriteString(&mol); if (inchi.empty() || inchi1.find(inchi) == inchi1.end()) { std::cout << name2 << " " << i << std::endl; } mol.Clear(); notatend = obconversion.Read(&mol); i++; } } int main(int argc,char **argv) { if(argc<3) { std::cerr << "Usage: " << argv[0] <<" ground_truth/ computed/" << std::endl; return 1; } OpenBabel::obErrorLog.StopLogging(); std::string folder1(argv[1]); std::string folder2(argv[2]); DIR *dir; struct dirent *ent; size_t total = 0, identical = 0, computed = 0; if ((dir = opendir (folder1.c_str())) != NULL) { while ((ent = readdir (dir)) != NULL) { std::string name1 = folder1 + ent->d_name; std::string name2 = folder2 + ent->d_name; if (name1.size() > 4 && name1.substr(name1.size()-4) == ".sdf") { std::set inchi1; collect_inchi(inchi1,name1); print_errors(inchi1,name2); } } closedir (dir); } else { std::cerr << "Unable to open directory " << argv[1] << std::endl; return 1; } return(0); } osra-2.1.3/src/CImg.h0000664000175000017500000564730014115175251012745 0ustar igorigor/* # # File : CImg.h # ( C++ header file ) # # Description : The C++ Template Image Processing Library # ( http://cimg.sourceforge.net ) # # Copyright : David Tschumperle # ( http://www.greyc.ensicaen.fr/~dtschump/ ) # # License : CeCILL-C # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html ) # # This software is governed by the CeCILL-C license under French law and # abiding by the rules of distribution of free software. You can use, # modify and or redistribute the software under the terms of the CeCILL-C # license as circulated by CEA, CNRS and INRIA at the following URL # "http://www.cecill.info". # # As a counterpart to the access to the source code and rights to copy, # modify and redistribute granted by the license, users are provided only # with a limited warranty and the software's author, the holder of the # economic rights, and the successive licensors have only limited # liability. # # In this respect, the user's attention is drawn to the risks associated # with loading, using, modifying and/or developing or reproducing the # software by the user in light of its specific status of free software, # that may mean that it is complicated to manipulate, and that also # therefore means that it is reserved for developers and experienced # professionals having in-depth computer knowledge. Users are therefore # encouraged to load and test the software's suitability as regards their # requirements in conditions enabling the security of their systems and/or # data to be ensured and, more generally, to use and operate it in the # same conditions as regards security. # # The fact that you are presently reading this means that you have had # knowledge of the CeCILL-C license and that you accept its terms. # */ #ifndef cimg_version #define cimg_version 127 // Detect Microsoft VC++ 6.0 compiler to get some workarounds afterwards. #if defined(_MSC_VER) && _MSC_VER<1300 #define cimg_use_visualcpp6 #endif // Try to avoid strange 'deprecated' warning messages with Visual C++ .NET. #if defined(_MSC_VER) && _MSC_VER>=1300 #define _CRT_SECURE_NO_DEPRECATE 1 #define _CRT_NONSTDC_NO_DEPRECATE 1 #endif // Standard C++ includes. #include #include #include #include #include #include // Custom OSRA settings. #include "config.h" /* # # Set CImg configuration flags. # # If compilation flags are not adapted to your system, # you may override their values, before including # the header file "CImg.h" (use the #define directive). # */ // Try to detect the current system and set value of 'cimg_OS'. #ifndef cimg_OS // Unix-like (Linux, Solaris, BSD, MacOSX, Irix,...). #if defined(unix) || defined(__unix) || defined(__unix__) \ || defined(linux) || defined(__linux) || defined(__linux__) \ || defined(sun) || defined(__sun) \ || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined __DragonFly__ \ || defined(__MACOSX__) || defined(__APPLE__) \ || defined(sgi) || defined(__sgi) \ || defined(__CYGWIN__) #define cimg_OS 1 #ifndef cimg_display_type #define cimg_display_type 1 #endif #ifndef cimg_color_terminal #define cimg_color_terminal #endif // Windows. #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \ || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) #define cimg_OS 2 #ifndef cimg_display_type #define cimg_display_type 2 #endif // Other (Unknown) configurations : set to minimal dependencies (no display). #else #define cimg_OS 0 #ifndef cimg_display_type #define cimg_display_type 0 #endif #endif #endif // Debug configuration. // // Set 'cimg_debug' to : 0 to remove debug messages (exceptions are still thrown anyway). // 1 to display debug messages on standard error output (console). // 2 to display debug messages in modal windows (default behavior). // 3 to do as 2 + add extra memory access warnings (may slow down the code) #ifndef cimg_debug #define cimg_debug 2 #endif // Architecture-dependent includes. #if cimg_OS==1 #include #include #elif cimg_OS==2 #include #ifndef _WIN32_IE #define _WIN32_IE 0x0400 #endif #include #ifdef cimg_use_visualcpp6 #define std #endif #endif // Test if min/max or PI macros are defined. #ifdef PI #error ------------------------------------------------------------------------------- #error The macro value 'PI' has been defined prior to the #include "CImg.h" directive. #error The CImg Library does not compile with such a macro value defined. #error Please (re)define this macro *after* including "CImg.h" if really necessary. #error Following error messages are most probably related to this problem. #error ------------------------------------------------------------------------------- #endif #ifdef min #undef min #define cimg_redefine_min #endif #ifdef max #undef max #define cimg_redefine_max #endif // Display-dependent includes. #if cimg_display_type==1 #include #include #include #include #ifdef cimg_use_xshm #include #include #include #endif #ifdef cimg_use_xrandr #include #endif #endif // Configuration for using extra libraries // // Define 'cimg_use_png', 'cimg_use_jpeg' or 'cimg_use_tiff' to enable native PNG, JPEG or TIFF files support. // This requires you link your code with the zlib/png, jpeg or tiff libraries. // Without these libraries, PNG,JPEG and TIFF support will be done by the Image Magick's 'convert' tool, // or byt the GraphicsMagick 'gm' tool if installed // (this is the case on most unix plateforms). #ifdef cimg_use_png extern "C" { #include "png.h" } #endif #ifdef cimg_use_jpeg extern "C" { #include "jpeglib.h" } #endif #ifdef cimg_use_tiff extern "C" { #include "tiffio.h" } #endif #ifdef cimg_use_magick #include "Magick++.h" #endif #ifdef cimg_use_fftw3 extern "C" { #include "fftw3.h" } #endif #ifdef cimg_use_lapack extern "C" { extern void sgetrf_(int*, int*, float*, int*, int*, int*); extern void sgetri_(int*, float*, int*, int*, float*, int*, int*); extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*); extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*); extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*); extern void dgetrf_(int*, int*, double*, int*, int*, int*); extern void dgetri_(int*, double*, int*, int*, double*, int*, int*); extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*); extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*); extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*); } #endif /* # # # Define some useful macros. Macros of the CImg Library are prefixed by 'cimg_' # Documented macros below may be safely used in your own code # (particularly useful for option parsing, image loops and neighborhoods). # # */ // Macros used to describe the program usage, and retrieve command line arguments // (See corresponding module 'Retrieving command line arguments' in the generated documentation). #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage) #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0) #define cimg_option(name,defaut,usage) cimg_library::cimg::option(name,argc,argv,defaut,usage) #define cimg_argument(pos) cimg_library::cimg::argument(pos,argc,argv) #define cimg_argument1(pos,s0) cimg_library::cimg::argument(pos,argc,argv,1,s0) #define cimg_argument2(pos,s0,s1) cimg_library::cimg::argument(pos,argc,argv,2,s0,s1) #define cimg_argument3(pos,s0,s1,s2) cimg_library::cimg::argument(pos,argc,argv,3,s0,s1,s2) #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3) #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4) #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5) #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6) #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7) #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8) // Macros used for neighborhood definitions and manipulations. // (see module 'Using Image Loops' in the generated documentation). #define CImg_2x2(I,T) T I[4]; \ T& I##cc = I[0]; T& I##nc = I[1]; \ T& I##cn = I[2]; T& I##nn = I[3]; \ I##cc = I##nc = \ I##cn = I##nn = 0 #define CImg_3x3(I,T) T I[9]; \ T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \ T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \ T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \ I##pp = I##cp = I##np = \ I##pc = I##cc = I##nc = \ I##pn = I##cn = I##nn = 0 #define CImg_4x4(I,T) T I[16]; \ T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \ T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \ T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \ T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \ I##pp = I##cp = I##np = I##ap = \ I##pc = I##cc = I##nc = I##ac = \ I##pn = I##cn = I##nn = I##an = \ I##pa = I##ca = I##na = I##aa = 0 #define CImg_5x5(I,T) T I[25]; \ T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \ T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \ T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \ T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \ T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \ I##bb = I##pb = I##cb = I##nb = I##ab = \ I##bp = I##pp = I##cp = I##np = I##ap = \ I##bc = I##pc = I##cc = I##nc = I##ac = \ I##bn = I##pn = I##cn = I##nn = I##an = \ I##ba = I##pa = I##ca = I##na = I##aa = 0 #define CImg_2x2x2(I,T) T I[8]; \ T& I##ccc = I[0]; T& I##ncc = I[1]; \ T& I##cnc = I[2]; T& I##nnc = I[3]; \ T& I##ccn = I[4]; T& I##ncn = I[5]; \ T& I##cnn = I[6]; T& I##nnn = I[7]; \ I##ccc = I##ncc = \ I##cnc = I##nnc = \ I##ccn = I##ncn = \ I##cnn = I##nnn = 0 #define CImg_3x3x3(I,T) T I[27]; \ T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \ T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \ T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \ T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \ T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \ T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \ T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \ T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \ T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \ I##ppp = I##cpp = I##npp = \ I##pcp = I##ccp = I##ncp = \ I##pnp = I##cnp = I##nnp = \ I##ppc = I##cpc = I##npc = \ I##pcc = I##ccc = I##ncc = \ I##pnc = I##cnc = I##nnc = \ I##ppn = I##cpn = I##npn = \ I##pcn = I##ccn = I##ncn = \ I##pnn = I##cnn = I##nnn = 0 #define cimg_get2x2(img,x,y,z,v,I) \ I[0] = (img)(x, y,z,v), I[1] = (img)(_n1##x, y,z,v), \ I[2] = (img)(x,_n1##y,z,v), I[3] = (img)(_n1##x,_n1##y,z,v) #define cimg_get3x3(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), \ I[3] = (img)(_p1##x, y,z,v), I[4] = (img)(x, y,z,v), I[5] = (img)(_n1##x, y,z,v), \ I[6] = (img)(_p1##x,_n1##y,z,v), I[7] = (img)(x,_n1##y,z,v), I[8] = (img)(_n1##x,_n1##y,z,v) #define cimg_get4x4(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,z,v), I[1] = (img)(x,_p1##y,z,v), I[2] = (img)(_n1##x,_p1##y,z,v), I[3] = (img)(_n2##x,_p1##y,z,v), \ I[4] = (img)(_p1##x, y,z,v), I[5] = (img)(x, y,z,v), I[6] = (img)(_n1##x, y,z,v), I[7] = (img)(_n2##x, y,z,v), \ I[8] = (img)(_p1##x,_n1##y,z,v), I[9] = (img)(x,_n1##y,z,v), I[10] = (img)(_n1##x,_n1##y,z,v), I[11] = (img)(_n2##x,_n1##y,z,v), \ I[12] = (img)(_p1##x,_n2##y,z,v), I[13] = (img)(x,_n2##y,z,v), I[14] = (img)(_n1##x,_n2##y,z,v), I[15] = (img)(_n2##x,_n2##y,z,v) #define cimg_get5x5(img,x,y,z,v,I) \ I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), I[4] = (img)(_n2##x,_p2##y,z,v), \ I[5] = (img)(_p2##x,_p1##y,z,v), I[6] = (img)(_p1##x,_p1##y,z,v), I[7] = (img)(x,_p1##y,z,v), I[8] = (img)(_n1##x,_p1##y,z,v), I[9] = (img)(_n2##x,_p1##y,z,v), \ I[10] = (img)(_p2##x, y,z,v), I[11] = (img)(_p1##x, y,z,v), I[12] = (img)(x, y,z,v), I[13] = (img)(_n1##x, y,z,v), I[14] = (img)(_n2##x, y,z,v), \ I[15] = (img)(_p2##x,_n1##y,z,v), I[16] = (img)(_p1##x,_n1##y,z,v), I[17] = (img)(x,_n1##y,z,v), I[18] = (img)(_n1##x,_n1##y,z,v), I[19] = (img)(_n2##x,_n1##y,z,v), \ I[20] = (img)(_p2##x,_n2##y,z,v), I[21] = (img)(_p1##x,_n2##y,z,v), I[22] = (img)(x,_n2##y,z,v), I[23] = (img)(_n1##x,_n2##y,z,v), I[24] = (img)(_n2##x,_n2##y,z,v) #define cimg_get6x6(img,x,y,z,v,I) \ I[0] = (img)(_p2##x,_p2##y,z,v), I[1] = (img)(_p1##x,_p2##y,z,v), I[2] = (img)(x,_p2##y,z,v), I[3] = (img)(_n1##x,_p2##y,z,v), I[4] = (img)(_n2##x,_p2##y,z,v), I[5] = (img)(_n3##x,_p2##y,z,v), \ I[6] = (img)(_p2##x,_p1##y,z,v), I[7] = (img)(_p1##x,_p1##y,z,v), I[8] = (img)(x,_p1##y,z,v), I[9] = (img)(_n1##x,_p1##y,z,v), I[10] = (img)(_n2##x,_p1##y,z,v), I[11] = (img)(_n3##x,_p1##y,z,v), \ I[12] = (img)(_p2##x,y,z,v), I[13] = (img)(_p1##x,y,z,v), I[14] = (img)(x,y,z,v), I[15] = (img)(_n1##x,y,z,v), I[16] = (img)(_n2##x,y,z,v), I[17] = (img)(_n3##x,y,z,v), \ I[18] = (img)(_p2##x,_n1##y,z,v), I[19] = (img)(_p1##x,_n1##y,z,v), I[20] = (img)(x,_n1##y,z,v), I[21] = (img)(_n1##x,_n1##y,z,v), I[22] = (img)(_n2##x,_n1##y,z,v), I[23] = (img)(_n3##x,_n1##y,z,v), \ I[24] = (img)(_p2##x,_n2##y,z,v), I[25] = (img)(_p1##x,_n2##y,z,v), I[26] = (img)(x,_n2##y,z,v), I[27] = (img)(_n1##x,_n2##y,z,v), I[28] = (img)(_n2##x,_n2##y,z,v), I[29] = (img)(_n3##x,_n2##y,z,v), \ I[30] = (img)(_p2##x,_n3##y,z,v), I[31] = (img)(_p1##x,_n3##y,z,v), I[32] = (img)(x,_n3##y,z,v), I[33] = (img)(_n1##x,_n3##y,z,v), I[34] = (img)(_n2##x,_n3##y,z,v), I[35] = (img)(_n3##x,_n3##y,z,v); #define cimg_get7x7(img,x,y,z,v,I) \ I[0] = (img)(_p3##x,_p3##y,z,v), I[1] = (img)(_p2##x,_p3##y,z,v), I[2] = (img)(_p1##x,_p3##y,z,v), I[3] = (img)(x,_p3##y,z,v), I[4] = (img)(_n1##x,_p3##y,z,v), I[5] = (img)(_n2##x,_p3##y,z,v), I[6] = (img)(_n3##x,_p3##y,z,v), \ I[7] = (img)(_p3##x,_p2##y,z,v), I[8] = (img)(_p2##x,_p2##y,z,v), I[9] = (img)(_p1##x,_p2##y,z,v), I[10] = (img)(x,_p2##y,z,v), I[11] = (img)(_n1##x,_p2##y,z,v), I[12] = (img)(_n2##x,_p2##y,z,v), I[13] = (img)(_n3##x,_p2##y,z,v), \ I[14] = (img)(_p3##x,_p1##y,z,v), I[15] = (img)(_p2##x,_p1##y,z,v), I[16] = (img)(_p1##x,_p1##y,z,v), I[17] = (img)(x,_p1##y,z,v), I[18] = (img)(_n1##x,_p1##y,z,v), I[19] = (img)(_n2##x,_p1##y,z,v), I[20] = (img)(_n3##x,_p1##y,z,v), \ I[21] = (img)(_p3##x,y,z,v), I[22] = (img)(_p2##x,y,z,v), I[23] = (img)(_p1##x,y,z,v), I[24] = (img)(x,y,z,v), I[25] = (img)(_n1##x,y,z,v), I[26] = (img)(_n2##x,y,z,v), I[27] = (img)(_n3##x,y,z,v), \ I[28] = (img)(_p3##x,_n1##y,z,v), I[29] = (img)(_p2##x,_n1##y,z,v), I[30] = (img)(_p1##x,_n1##y,z,v), I[31] = (img)(x,_n1##y,z,v), I[32] = (img)(_n1##x,_n1##y,z,v), I[33] = (img)(_n2##x,_n1##y,z,v), I[34] = (img)(_n3##x,_n1##y,z,v), \ I[35] = (img)(_p3##x,_n2##y,z,v), I[36] = (img)(_p2##x,_n2##y,z,v), I[37] = (img)(_p1##x,_n2##y,z,v), I[38] = (img)(x,_n2##y,z,v), I[39] = (img)(_n1##x,_n2##y,z,v), I[40] = (img)(_n2##x,_n2##y,z,v), I[41] = (img)(_n3##x,_n2##y,z,v), \ I[42] = (img)(_p3##x,_n3##y,z,v), I[43] = (img)(_p2##x,_n3##y,z,v), I[44] = (img)(_p1##x,_n3##y,z,v), I[45] = (img)(x,_n3##y,z,v), I[46] = (img)(_n1##x,_n3##y,z,v), I[47] = (img)(_n2##x,_n3##y,z,v), I[48] = (img)(_n3##x,_n3##y,z,v); #define cimg_get2x2x2(img,x,y,z,v,I) \ I[0] = (img)(x, y, z,v), I[1] = (img)(_n1##x, y, z,v), \ I[2] = (img)(x,_n1##y, z,v), I[3] = (img)(_n1##x,_n1##y, z,v), \ I[4] = (img)(x, y,_n1##z,v), I[5] = (img)(_n1##x, y,_n1##z,v), \ I[6] = (img)(x,_n1##y,_n1##z,v), I[7] = (img)(_n1##x,_n1##y,_n1##z,v) #define cimg_get3x3x3(img,x,y,z,v,I) \ I[0] = (img)(_p1##x,_p1##y,_p1##z,v), I[1] = (img)(x,_p1##y,_p1##z,v), I[2] = (img)(_n1##x,_p1##y,_p1##z,v), \ I[3] = (img)(_p1##x, y,_p1##z,v), I[4] = (img)(x, y,_p1##z,v), I[5] = (img)(_n1##x, y,_p1##z,v), \ I[6] = (img)(_p1##x,_n1##y,_p1##z,v), I[7] = (img)(x,_n1##y,_p1##z,v), I[8] = (img)(_n1##x,_n1##y,_p1##z,v), \ I[9] = (img)(_p1##x,_p1##y, z,v), I[10] = (img)(x,_p1##y, z,v), I[11] = (img)(_n1##x,_p1##y, z,v), \ I[12] = (img)(_p1##x, y, z,v), I[13] = (img)(x, y, z,v), I[14] = (img)(_n1##x, y, z,v), \ I[15] = (img)(_p1##x,_n1##y, z,v), I[16] = (img)(x,_n1##y, z,v), I[17] = (img)(_n1##x,_n1##y, z,v), \ I[18] = (img)(_p1##x,_p1##y,_n1##z,v), I[19] = (img)(x,_p1##y,_n1##z,v), I[20] = (img)(_n1##x,_p1##y,_n1##z,v), \ I[21] = (img)(_p1##x, y,_n1##z,v), I[22] = (img)(x, y,_n1##z,v), I[23] = (img)(_n1##x, y,_n1##z,v), \ I[24] = (img)(_p1##x,_n1##y,_n1##z,v), I[25] = (img)(x,_n1##y,_n1##z,v), I[26] = (img)(_n1##x,_n1##y,_n1##z,v) // Macros used to define special image loops. // (see module 'Using Image Loops' in the generated documentation). // (it looks ugly, but it is actually ..sooooooo... useful !) #define cimg_for(img,ptr,T_ptr) for (T_ptr *ptr = (img).data + (img).size(); (ptr--)>(img).data; ) #define cimg_foroff(img,off) for (unsigned int off = 0; off<(img).size(); ++off) #define cimglist_for(list,l) for (unsigned int l=0; l<(list).size; ++l) #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i) #define cimg_forX(img,x) cimg_for1((img).width,x) #define cimg_forY(img,y) cimg_for1((img).height,y) #define cimg_forZ(img,z) cimg_for1((img).depth,z) #define cimg_forV(img,v) cimg_for1((img).dim,v) #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x) #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x) #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y) #define cimg_forXV(img,x,v) cimg_forV(img,v) cimg_forX(img,x) #define cimg_forYV(img,y,v) cimg_forV(img,v) cimg_forY(img,y) #define cimg_forZV(img,z,v) cimg_forV(img,v) cimg_forZ(img,z) #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y) #define cimg_forXYV(img,x,y,v) cimg_forV(img,v) cimg_forXY(img,x,y) #define cimg_forXZV(img,x,z,v) cimg_forV(img,v) cimg_forXZ(img,x,z) #define cimg_forYZV(img,y,z,v) cimg_forV(img,v) cimg_forYZ(img,y,z) #define cimg_forXYZV(img,x,y,z,v) cimg_forV(img,v) cimg_forXYZ(img,x,y,z) #define cimg_for_in1(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i) #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img).width,x0,x1,x) #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img).height,y0,y1,y) #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img).depth,z0,z1,z) #define cimg_for_inV(img,v0,v1,v) cimg_for_in1((img).dim,v0,v1,v) #define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x) #define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x) #define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y) #define cimg_for_inXV(img,x0,v0,x1,v1,x,v) cimg_for_inV(img,v0,v1,v) cimg_for_inX(img,x0,x1,x) #define cimg_for_inYV(img,y0,v0,y1,v1,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inY(img,y0,y1,y) #define cimg_for_inZV(img,z0,v0,z1,v1,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inZ(img,z0,z1,z) #define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y) #define cimg_for_inXYV(img,x0,y0,v0,x1,y1,v1,x,y,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXY(img,x0,y0,x1,y1,x,y) #define cimg_for_inXZV(img,x0,z0,v0,x1,z1,v1,x,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXZ(img,x0,z0,x1,z1,x,z) #define cimg_for_inYZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inYZ(img,y0,z0,y1,z1,y,z) #define cimg_for_inXYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_inV(img,v0,v1,v) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for_out1(bound,i0,i1,i) \ for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(bound); ++i, i = i==(int)(i0)?(int)(i1)+1:i) #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img).width,x0,x1,x) #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img).height,y0,y1,y) #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img).depth,z0,z1,z) #define cimg_for_outV(img,v0,v1,v) cimg_for_out1((img).dim,v0,v1,v) #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) \ for (int y = (int)(y0)>0?0:((int)(x0)>0 || (int)(x1)<(int)(img).width-1?0:(int)(y1)+1),\ _min##x = (int)(x0)>0?0:(int)(x1)+1; y<(int)(img).height; ++y) \ for (int x = y>=(int)(y0) && y<=(int)(y1)?_min##x:0; x<(int)(img).width; ++x, \ x = y>=(int)y0 && y<=(int)y1 && x==(int)(x0)?(int)(x1)+1:x) #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_forZ(img,z) \ for (int y = z<(int)(z0) || z>(int)z1?0:((int)(y0)>0?0:((int)(x0)>0 || (int)(x1)<(int)(img).width-1?0:(int)(y1)+1)),\ _min##x = (int)(x0)>0?0:(int)(x1)+1; y<(int)(img).height; ++y) \ for (int x = z<(int)(z0) || z>(int)z1?0:(y>=(int)(y0) && y<=(int)(y1)?_min##x:0); x<(int)(img).width; ++x, \ x = z>=(int)z0 && z<=(int)z1 && y>=(int)y0 && y<=(int)y1 && x==(int)(x0)?(int)(x1)+1:x) #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img).width-1-(n),x) #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img).height-1-(n),y) #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img).depth-1-(n),z) #define cimg_for_insideV(img,v,n) cimg_for_inV(img,n,(img).dim-1-(n),v) #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) #define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img).width-1-(n),x) #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img).height-1-(n),y) #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img).depth-1-(n),z) #define cimg_for_borderV(img,v,n) cimg_for_outV(img,n,(img).dim-1-(n),v) #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img).width-1-(n),(img).height-1-(n),x,y) #define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img).width-1-(n),(img).height-1-(n),(img).depth-1-(n),x,y,z) #define cimg_for_spiralXY(img,x,y) \ for (int x = 0, y = 0, _n1##x = 1, _n1##y = (int)((img).width*(img).height); _n1##y; \ --_n1##y, _n1##x += (_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img).width-1-++x:((_n1##x&3)==2?(img).height-1-++y:--x))))?0:1) #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \ for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \ _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \ _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \ _counter = _dx, \ _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \ _counter>=0; \ --_counter, x+=_steep? \ (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \ (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx)) #define cimg_for2(bound,i) \ for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ ++i, ++_n1##i) #define cimg_for2X(img,x) cimg_for2((img).width,x) #define cimg_for2Y(img,y) cimg_for2((img).height,y) #define cimg_for2Z(img,z) cimg_for2((img).depth,z) #define cimg_for2V(img,v) cimg_for2((img).dim,v) #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x) #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x) #define cimg_for2XV(img,x,v) cimg_for2V(img,v) cimg_for2X(img,x) #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y) #define cimg_for2YV(img,y,v) cimg_for2V(img,v) cimg_for2Y(img,y) #define cimg_for2ZV(img,z,v) cimg_for2V(img,v) cimg_for2Z(img,z) #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y) #define cimg_for2XZV(img,x,z,v) cimg_for2V(img,v) cimg_for2XZ(img,x,z) #define cimg_for2YZV(img,y,z,v) cimg_for2V(img,v) cimg_for2YZ(img,y,z) #define cimg_for2XYZV(img,x,y,z,v) cimg_for2V(img,v) cimg_for2XYZ(img,x,y,z) #define cimg_for_in2(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ ++i, ++_n1##i) #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img).width,x0,x1,x) #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img).height,y0,y1,y) #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img).depth,z0,z1,z) #define cimg_for_in2V(img,v0,v1,v) cimg_for_in2((img).dim,v0,v1,v) #define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x) #define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x) #define cimg_for_in2XV(img,x0,v0,x1,v1,x,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2X(img,x0,x1,x) #define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y) #define cimg_for_in2YV(img,y0,v0,y1,v1,y,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Y(img,y0,y1,y) #define cimg_for_in2ZV(img,z0,v0,z1,v1,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2Z(img,z0,z1,z) #define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in2XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in2YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in2XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in2V(img,v0,v1,v) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for3(bound,i) \ for (int i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1; \ _n1##i<(int)(bound) || i==--_n1##i; \ _p1##i = i++, ++_n1##i) #define cimg_for3X(img,x) cimg_for3((img).width,x) #define cimg_for3Y(img,y) cimg_for3((img).height,y) #define cimg_for3Z(img,z) cimg_for3((img).depth,z) #define cimg_for3V(img,v) cimg_for3((img).dim,v) #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x) #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x) #define cimg_for3XV(img,x,v) cimg_for3V(img,v) cimg_for3X(img,x) #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y) #define cimg_for3YV(img,y,v) cimg_for3V(img,v) cimg_for3Y(img,y) #define cimg_for3ZV(img,z,v) cimg_for3V(img,v) cimg_for3Z(img,z) #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y) #define cimg_for3XZV(img,x,z,v) cimg_for3V(img,v) cimg_for3XZ(img,x,z) #define cimg_for3YZV(img,y,z,v) cimg_for3V(img,v) cimg_for3YZ(img,y,z) #define cimg_for3XYZV(img,x,y,z,v) cimg_for3V(img,v) cimg_for3XYZ(img,x,y,z) #define cimg_for_in3(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \ i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \ _p1##i = i++, ++_n1##i) #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img).width,x0,x1,x) #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img).height,y0,y1,y) #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img).depth,z0,z1,z) #define cimg_for_in3V(img,v0,v1,v) cimg_for_in3((img).dim,v0,v1,v) #define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x) #define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x) #define cimg_for_in3XV(img,x0,v0,x1,v1,x,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3X(img,x0,x1,x) #define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y) #define cimg_for_in3YV(img,y0,v0,y1,v1,y,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Y(img,y0,y1,y) #define cimg_for_in3ZV(img,z0,v0,z1,v1,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3Z(img,z0,z1,z) #define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in3XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in3YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in3XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in3V(img,v0,v1,v) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for4(bound,i) \ for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2; \ _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for4X(img,x) cimg_for4((img).width,x) #define cimg_for4Y(img,y) cimg_for4((img).height,y) #define cimg_for4Z(img,z) cimg_for4((img).depth,z) #define cimg_for4V(img,v) cimg_for4((img).dim,v) #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x) #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x) #define cimg_for4XV(img,x,v) cimg_for4V(img,v) cimg_for4X(img,x) #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y) #define cimg_for4YV(img,y,v) cimg_for4V(img,v) cimg_for4Y(img,y) #define cimg_for4ZV(img,z,v) cimg_for4V(img,v) cimg_for4Z(img,z) #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y) #define cimg_for4XZV(img,x,z,v) cimg_for4V(img,v) cimg_for4XZ(img,x,z) #define cimg_for4YZV(img,y,z,v) cimg_for4V(img,v) cimg_for4YZ(img,y,z) #define cimg_for4XYZV(img,x,y,z,v) cimg_for4V(img,v) cimg_for4XYZ(img,x,y,z) #define cimg_for_in4(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img).width,x0,x1,x) #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img).height,y0,y1,y) #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img).depth,z0,z1,z) #define cimg_for_in4V(img,v0,v1,v) cimg_for_in4((img).dim,v0,v1,v) #define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x) #define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x) #define cimg_for_in4XV(img,x0,v0,x1,v1,x,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4X(img,x0,x1,x) #define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y) #define cimg_for_in4YV(img,y0,v0,y1,v1,y,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Y(img,y0,y1,y) #define cimg_for_in4ZV(img,z0,v0,z1,v1,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4Z(img,z0,z1,z) #define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in4XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in4YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in4XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in4V(img,v0,v1,v) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for5(bound,i) \ for (int i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2; \ _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for5X(img,x) cimg_for5((img).width,x) #define cimg_for5Y(img,y) cimg_for5((img).height,y) #define cimg_for5Z(img,z) cimg_for5((img).depth,z) #define cimg_for5V(img,v) cimg_for5((img).dim,v) #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x) #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x) #define cimg_for5XV(img,x,v) cimg_for5V(img,v) cimg_for5X(img,x) #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y) #define cimg_for5YV(img,y,v) cimg_for5V(img,v) cimg_for5Y(img,y) #define cimg_for5ZV(img,z,v) cimg_for5V(img,v) cimg_for5Z(img,z) #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y) #define cimg_for5XZV(img,x,z,v) cimg_for5V(img,v) cimg_for5XZ(img,x,z) #define cimg_for5YZV(img,y,z,v) cimg_for5V(img,v) cimg_for5YZ(img,y,z) #define cimg_for5XYZV(img,x,y,z,v) cimg_for5V(img,v) cimg_for5XYZ(img,x,y,z) #define cimg_for_in5(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \ i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i) #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img).width,x0,x1,x) #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img).height,y0,y1,y) #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img).depth,z0,z1,z) #define cimg_for_in5V(img,v0,v1,v) cimg_for_in5((img).dim,v0,v1,v) #define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x) #define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x) #define cimg_for_in5XV(img,x0,v0,x1,v1,x,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5X(img,x0,x1,x) #define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y) #define cimg_for_in5YV(img,y0,v0,y1,v1,y,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Y(img,y0,y1,y) #define cimg_for_in5ZV(img,z0,v0,z1,v1,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5Z(img,z0,z1,z) #define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in5XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in5YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in5XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in5V(img,v0,v1,v) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for6(bound,i) \ for (int i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(bound)?(int)(bound)-1:3; \ _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for6X(img,x) cimg_for6((img).width,x) #define cimg_for6Y(img,y) cimg_for6((img).height,y) #define cimg_for6Z(img,z) cimg_for6((img).depth,z) #define cimg_for6V(img,v) cimg_for6((img).dim,v) #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x) #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x) #define cimg_for6XV(img,x,v) cimg_for6V(img,v) cimg_for6X(img,x) #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y) #define cimg_for6YV(img,y,v) cimg_for6V(img,v) cimg_for6Y(img,y) #define cimg_for6ZV(img,z,v) cimg_for6V(img,v) cimg_for6Z(img,z) #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y) #define cimg_for6XZV(img,x,z,v) cimg_for6V(img,v) cimg_for6XZ(img,x,z) #define cimg_for6YZV(img,y,z,v) cimg_for6V(img,v) cimg_for6YZ(img,y,z) #define cimg_for6XYZV(img,x,y,z,v) cimg_for6V(img,v) cimg_for6XYZ(img,x,y,z) #define cimg_for_in6(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img).width,x0,x1,x) #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img).height,y0,y1,y) #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img).depth,z0,z1,z) #define cimg_for_in6V(img,v0,v1,v) cimg_for_in6((img).dim,v0,v1,v) #define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x) #define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x) #define cimg_for_in6XV(img,x0,v0,x1,v1,x,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6X(img,x0,x1,x) #define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y) #define cimg_for_in6YV(img,y0,v0,y1,v1,y,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Y(img,y0,y1,y) #define cimg_for_in6ZV(img,z0,v0,z1,v1,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6Z(img,z0,z1,z) #define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in6XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in6YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in6XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in6V(img,v0,v1,v) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for7(bound,i) \ for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \ _n1##i = 1>=(bound)?(int)(bound)-1:1, \ _n2##i = 2>=(bound)?(int)(bound)-1:2, \ _n3##i = 3>=(bound)?(int)(bound)-1:3; \ _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for7X(img,x) cimg_for7((img).width,x) #define cimg_for7Y(img,y) cimg_for7((img).height,y) #define cimg_for7Z(img,z) cimg_for7((img).depth,z) #define cimg_for7V(img,v) cimg_for7((img).dim,v) #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x) #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x) #define cimg_for7XV(img,x,v) cimg_for7V(img,v) cimg_for7X(img,x) #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y) #define cimg_for7YV(img,y,v) cimg_for7V(img,v) cimg_for7Y(img,y) #define cimg_for7ZV(img,z,v) cimg_for7V(img,v) cimg_for7Z(img,z) #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y) #define cimg_for7XZV(img,x,z,v) cimg_for7V(img,v) cimg_for7XZ(img,x,z) #define cimg_for7YZV(img,y,z,v) cimg_for7V(img,v) cimg_for7YZ(img,y,z) #define cimg_for7XYZV(img,x,y,z,v) cimg_for7V(img,v) cimg_for7XYZ(img,x,y,z) #define cimg_for_in7(bound,i0,i1,i) \ for (int i = (int)(i0)<0?0:(int)(i0), \ _p3##i = i-3<0?0:i-3, \ _p2##i = i-2<0?0:i-2, \ _p1##i = i-1<0?0:i-1, \ _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \ _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \ _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \ i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \ _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i) #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img).width,x0,x1,x) #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img).height,y0,y1,y) #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img).depth,z0,z1,z) #define cimg_for_in7V(img,v0,v1,v) cimg_for_in7((img).dim,v0,v1,v) #define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x) #define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x) #define cimg_for_in7XV(img,x0,v0,x1,v1,x,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7X(img,x0,x1,x) #define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y) #define cimg_for_in7YV(img,y0,v0,y1,v1,y,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Y(img,y0,y1,y) #define cimg_for_in7ZV(img,z0,v0,z1,v1,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7Z(img,z0,z1,z) #define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y) #define cimg_for_in7XZV(img,x0,z0,v0,x1,y1,v1,x,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z) #define cimg_for_in7YZV(img,y0,z0,v0,y1,z1,v1,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) #define cimg_for_in7XYZV(img,x0,y0,z0,v0,x1,y1,z1,v1,x,y,z,v) cimg_for_in7V(img,v0,v1,v) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) #define cimg_for2x2(img,x,y,z,v,I) \ cimg_for2((img).height,y) for (int x = 0, \ _n1##x = (int)( \ (I[0] = (img)(0,y,z,v)), \ (I[2] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], \ I[2] = I[3], \ ++x, ++_n1##x) #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in2((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _n1##x = (int)( \ (I[0] = (img)(x,y,z,v)), \ (I[2] = (img)(x,_n1##y,z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], \ I[2] = I[3], \ ++x, ++_n1##x) #define cimg_for3x3(img,x,y,z,v,I) \ cimg_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ (I[3] = I[4] = (img)(0,y,z,v)), \ (I[6] = I[7] = (img)(0,_n1##y,z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ _p1##x = x++, ++_n1##x) #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in3((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p1##x = x-1<0?0:x-1, \ _n1##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,z,v)), \ (I[3] = (img)(_p1##x,y,z,v)), \ (I[6] = (img)(_p1##x,_n1##y,z,v)), \ (I[1] = (img)(x,_p1##y,z,v)), \ (I[4] = (img)(x,y,z,v)), \ (I[7] = (img)(x,_n1##y,z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[5] = (img)(_n1##x,y,z,v)), \ (I[8] = (img)(_n1##x,_n1##y,z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[1] = I[2], \ I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], \ _p1##x = x++, ++_n1##x) #define cimg_for4x4(img,x,y,z,v,I) \ cimg_for4((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,z,v)), \ (I[4] = I[5] = (img)(0,y,z,v)), \ (I[8] = I[9] = (img)(0,_n1##y,z,v)), \ (I[12] = I[13] = (img)(0,_n2##y,z,v)), \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[6] = (img)(_n1##x,y,z,v)), \ (I[10] = (img)(_n1##x,_n1##y,z,v)), \ (I[14] = (img)(_n1##x,_n2##y,z,v)), \ 2>=(img).width?(int)((img).width)-1:2); \ (_n2##x<(int)((img).width) && ( \ (I[3] = (img)(_n2##x,_p1##y,z,v)), \ (I[7] = (img)(_n2##x,y,z,v)), \ (I[11] = (img)(_n2##x,_n1##y,z,v)), \ (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], \ I[4] = I[5], I[5] = I[6], I[6] = I[7], \ I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], \ _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in4((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,z,v)), \ (I[4] = (img)(_p1##x,y,z,v)), \ (I[8] = (img)(_p1##x,_n1##y,z,v)), \ (I[12] = (img)(_p1##x,_n2##y,z,v)), \ (I[1] = (img)(x,_p1##y,z,v)), \ (I[5] = (img)(x,y,z,v)), \ (I[9] = (img)(x,_n1##y,z,v)), \ (I[13] = (img)(x,_n2##y,z,v)), \ (I[2] = (img)(_n1##x,_p1##y,z,v)), \ (I[6] = (img)(_n1##x,y,z,v)), \ (I[10] = (img)(_n1##x,_n1##y,z,v)), \ (I[14] = (img)(_n1##x,_n2##y,z,v)), \ x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ (I[3] = (img)(_n2##x,_p1##y,z,v)), \ (I[7] = (img)(_n2##x,y,z,v)), \ (I[11] = (img)(_n2##x,_n1##y,z,v)), \ (I[15] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], \ I[4] = I[5], I[5] = I[6], I[6] = I[7], \ I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], \ _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for5x5(img,x,y,z,v,I) \ cimg_for5((img).height,y) for (int x = 0, \ _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = (int)( \ (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ (I[5] = I[6] = I[7] = (img)(0,_p1##y,z,v)), \ (I[10] = I[11] = I[12] = (img)(0,y,z,v)), \ (I[15] = I[16] = I[17] = (img)(0,_n1##y,z,v)), \ (I[20] = I[21] = I[22] = (img)(0,_n2##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[8] = (img)(_n1##x,_p1##y,z,v)), \ (I[13] = (img)(_n1##x,y,z,v)), \ (I[18] = (img)(_n1##x,_n1##y,z,v)), \ (I[23] = (img)(_n1##x,_n2##y,z,v)), \ 2>=(img).width?(int)((img).width)-1:2); \ (_n2##x<(int)((img).width) && ( \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[9] = (img)(_n2##x,_p1##y,z,v)), \ (I[14] = (img)(_n2##x,y,z,v)), \ (I[19] = (img)(_n2##x,_n1##y,z,v)), \ (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in5((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = (int)( \ (I[0] = (img)(_p2##x,_p2##y,z,v)), \ (I[5] = (img)(_p2##x,_p1##y,z,v)), \ (I[10] = (img)(_p2##x,y,z,v)), \ (I[15] = (img)(_p2##x,_n1##y,z,v)), \ (I[20] = (img)(_p2##x,_n2##y,z,v)), \ (I[1] = (img)(_p1##x,_p2##y,z,v)), \ (I[6] = (img)(_p1##x,_p1##y,z,v)), \ (I[11] = (img)(_p1##x,y,z,v)), \ (I[16] = (img)(_p1##x,_n1##y,z,v)), \ (I[21] = (img)(_p1##x,_n2##y,z,v)), \ (I[2] = (img)(x,_p2##y,z,v)), \ (I[7] = (img)(x,_p1##y,z,v)), \ (I[12] = (img)(x,y,z,v)), \ (I[17] = (img)(x,_n1##y,z,v)), \ (I[22] = (img)(x,_n2##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[8] = (img)(_n1##x,_p1##y,z,v)), \ (I[13] = (img)(_n1##x,y,z,v)), \ (I[18] = (img)(_n1##x,_n1##y,z,v)), \ (I[23] = (img)(_n1##x,_n2##y,z,v)), \ x+2>=(int)(img).width?(int)((img).width)-1:x+2); \ x<=(int)(x1) && ((_n2##x<(int)((img).width) && ( \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[9] = (img)(_n2##x,_p1##y,z,v)), \ (I[14] = (img)(_n2##x,y,z,v)), \ (I[19] = (img)(_n2##x,_n1##y,z,v)), \ (I[24] = (img)(_n2##x,_n2##y,z,v)),1)) || \ _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \ I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \ I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \ I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \ I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x) #define cimg_for6x6(img,x,y,z,v,I) \ cimg_for6((img).height,y) for (int x = 0, \ _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ _n3##x = (int)( \ (I[0] = I[1] = I[2] = (img)(0,_p2##y,z,v)), \ (I[6] = I[7] = I[8] = (img)(0,_p1##y,z,v)), \ (I[12] = I[13] = I[14] = (img)(0,y,z,v)), \ (I[18] = I[19] = I[20] = (img)(0,_n1##y,z,v)), \ (I[24] = I[25] = I[26] = (img)(0,_n2##y,z,v)), \ (I[30] = I[31] = I[32] = (img)(0,_n3##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[9] = (img)(_n1##x,_p1##y,z,v)), \ (I[15] = (img)(_n1##x,y,z,v)), \ (I[21] = (img)(_n1##x,_n1##y,z,v)), \ (I[27] = (img)(_n1##x,_n2##y,z,v)), \ (I[33] = (img)(_n1##x,_n3##y,z,v)), \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[10] = (img)(_n2##x,_p1##y,z,v)), \ (I[16] = (img)(_n2##x,y,z,v)), \ (I[22] = (img)(_n2##x,_n1##y,z,v)), \ (I[28] = (img)(_n2##x,_n2##y,z,v)), \ (I[34] = (img)(_n2##x,_n3##y,z,v)), \ 3>=(img).width?(int)((img).width)-1:3); \ (_n3##x<(int)((img).width) && ( \ (I[5] = (img)(_n3##x,_p2##y,z,v)), \ (I[11] = (img)(_n3##x,_p1##y,z,v)), \ (I[17] = (img)(_n3##x,y,z,v)), \ (I[23] = (img)(_n3##x,_n1##y,z,v)), \ (I[29] = (img)(_n3##x,_n2##y,z,v)), \ (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in6((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ _n3##x = (int)( \ (I[0] = (img)(_p2##x,_p2##y,z,v)), \ (I[6] = (img)(_p2##x,_p1##y,z,v)), \ (I[12] = (img)(_p2##x,y,z,v)), \ (I[18] = (img)(_p2##x,_n1##y,z,v)), \ (I[24] = (img)(_p2##x,_n2##y,z,v)), \ (I[30] = (img)(_p2##x,_n3##y,z,v)), \ (I[1] = (img)(_p1##x,_p2##y,z,v)), \ (I[7] = (img)(_p1##x,_p1##y,z,v)), \ (I[13] = (img)(_p1##x,y,z,v)), \ (I[19] = (img)(_p1##x,_n1##y,z,v)), \ (I[25] = (img)(_p1##x,_n2##y,z,v)), \ (I[31] = (img)(_p1##x,_n3##y,z,v)), \ (I[2] = (img)(x,_p2##y,z,v)), \ (I[8] = (img)(x,_p1##y,z,v)), \ (I[14] = (img)(x,y,z,v)), \ (I[20] = (img)(x,_n1##y,z,v)), \ (I[26] = (img)(x,_n2##y,z,v)), \ (I[32] = (img)(x,_n3##y,z,v)), \ (I[3] = (img)(_n1##x,_p2##y,z,v)), \ (I[9] = (img)(_n1##x,_p1##y,z,v)), \ (I[15] = (img)(_n1##x,y,z,v)), \ (I[21] = (img)(_n1##x,_n1##y,z,v)), \ (I[27] = (img)(_n1##x,_n2##y,z,v)), \ (I[33] = (img)(_n1##x,_n3##y,z,v)), \ (I[4] = (img)(_n2##x,_p2##y,z,v)), \ (I[10] = (img)(_n2##x,_p1##y,z,v)), \ (I[16] = (img)(_n2##x,y,z,v)), \ (I[22] = (img)(_n2##x,_n1##y,z,v)), \ (I[28] = (img)(_n2##x,_n2##y,z,v)), \ (I[34] = (img)(_n2##x,_n3##y,z,v)), \ x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ (I[5] = (img)(_n3##x,_p2##y,z,v)), \ (I[11] = (img)(_n3##x,_p1##y,z,v)), \ (I[17] = (img)(_n3##x,y,z,v)), \ (I[23] = (img)(_n3##x,_n1##y,z,v)), \ (I[29] = (img)(_n3##x,_n2##y,z,v)), \ (I[35] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \ I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \ I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \ I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \ I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \ _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for7x7(img,x,y,z,v,I) \ cimg_for7((img).height,y) for (int x = 0, \ _p3##x = 0, _p2##x = 0, _p1##x = 0, \ _n1##x = 1>=(img).width?(int)((img).width)-1:1, \ _n2##x = 2>=(img).width?(int)((img).width)-1:2, \ _n3##x = (int)( \ (I[0] = I[1] = I[2] = I[3] = (img)(0,_p3##y,z,v)), \ (I[7] = I[8] = I[9] = I[10] = (img)(0,_p2##y,z,v)), \ (I[14] = I[15] = I[16] = I[17] = (img)(0,_p1##y,z,v)), \ (I[21] = I[22] = I[23] = I[24] = (img)(0,y,z,v)), \ (I[28] = I[29] = I[30] = I[31] = (img)(0,_n1##y,z,v)), \ (I[35] = I[36] = I[37] = I[38] = (img)(0,_n2##y,z,v)), \ (I[42] = I[43] = I[44] = I[45] = (img)(0,_n3##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[11] = (img)(_n1##x,_p2##y,z,v)), \ (I[18] = (img)(_n1##x,_p1##y,z,v)), \ (I[25] = (img)(_n1##x,y,z,v)), \ (I[32] = (img)(_n1##x,_n1##y,z,v)), \ (I[39] = (img)(_n1##x,_n2##y,z,v)), \ (I[46] = (img)(_n1##x,_n3##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[12] = (img)(_n2##x,_p2##y,z,v)), \ (I[19] = (img)(_n2##x,_p1##y,z,v)), \ (I[26] = (img)(_n2##x,y,z,v)), \ (I[33] = (img)(_n2##x,_n1##y,z,v)), \ (I[40] = (img)(_n2##x,_n2##y,z,v)), \ (I[47] = (img)(_n2##x,_n3##y,z,v)), \ 3>=(img).width?(int)((img).width)-1:3); \ (_n3##x<(int)((img).width) && ( \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[13] = (img)(_n3##x,_p2##y,z,v)), \ (I[20] = (img)(_n3##x,_p1##y,z,v)), \ (I[27] = (img)(_n3##x,y,z,v)), \ (I[34] = (img)(_n3##x,_n1##y,z,v)), \ (I[41] = (img)(_n3##x,_n2##y,z,v)), \ (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,v,I) \ cimg_for_in7((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p3##x = x-3<0?0:x-3, \ _p2##x = x-2<0?0:x-2, \ _p1##x = x-1<0?0:x-1, \ _n1##x = x+1>=(int)(img).width?(int)((img).width)-1:x+1, \ _n2##x = x+2>=(int)(img).width?(int)((img).width)-1:x+2, \ _n3##x = (int)( \ (I[0] = (img)(_p3##x,_p3##y,z,v)), \ (I[7] = (img)(_p3##x,_p2##y,z,v)), \ (I[14] = (img)(_p3##x,_p1##y,z,v)), \ (I[21] = (img)(_p3##x,y,z,v)), \ (I[28] = (img)(_p3##x,_n1##y,z,v)), \ (I[35] = (img)(_p3##x,_n2##y,z,v)), \ (I[42] = (img)(_p3##x,_n3##y,z,v)), \ (I[1] = (img)(_p2##x,_p3##y,z,v)), \ (I[8] = (img)(_p2##x,_p2##y,z,v)), \ (I[15] = (img)(_p2##x,_p1##y,z,v)), \ (I[22] = (img)(_p2##x,y,z,v)), \ (I[29] = (img)(_p2##x,_n1##y,z,v)), \ (I[36] = (img)(_p2##x,_n2##y,z,v)), \ (I[43] = (img)(_p2##x,_n3##y,z,v)), \ (I[2] = (img)(_p1##x,_p3##y,z,v)), \ (I[9] = (img)(_p1##x,_p2##y,z,v)), \ (I[16] = (img)(_p1##x,_p1##y,z,v)), \ (I[23] = (img)(_p1##x,y,z,v)), \ (I[30] = (img)(_p1##x,_n1##y,z,v)), \ (I[37] = (img)(_p1##x,_n2##y,z,v)), \ (I[44] = (img)(_p1##x,_n3##y,z,v)), \ (I[3] = (img)(x,_p3##y,z,v)), \ (I[10] = (img)(x,_p2##y,z,v)), \ (I[17] = (img)(x,_p1##y,z,v)), \ (I[24] = (img)(x,y,z,v)), \ (I[31] = (img)(x,_n1##y,z,v)), \ (I[38] = (img)(x,_n2##y,z,v)), \ (I[45] = (img)(x,_n3##y,z,v)), \ (I[4] = (img)(_n1##x,_p3##y,z,v)), \ (I[11] = (img)(_n1##x,_p2##y,z,v)), \ (I[18] = (img)(_n1##x,_p1##y,z,v)), \ (I[25] = (img)(_n1##x,y,z,v)), \ (I[32] = (img)(_n1##x,_n1##y,z,v)), \ (I[39] = (img)(_n1##x,_n2##y,z,v)), \ (I[46] = (img)(_n1##x,_n3##y,z,v)), \ (I[5] = (img)(_n2##x,_p3##y,z,v)), \ (I[12] = (img)(_n2##x,_p2##y,z,v)), \ (I[19] = (img)(_n2##x,_p1##y,z,v)), \ (I[26] = (img)(_n2##x,y,z,v)), \ (I[33] = (img)(_n2##x,_n1##y,z,v)), \ (I[40] = (img)(_n2##x,_n2##y,z,v)), \ (I[47] = (img)(_n2##x,_n3##y,z,v)), \ x+3>=(int)(img).width?(int)((img).width)-1:x+3); \ x<=(int)(x1) && ((_n3##x<(int)((img).width) && ( \ (I[6] = (img)(_n3##x,_p3##y,z,v)), \ (I[13] = (img)(_n3##x,_p2##y,z,v)), \ (I[20] = (img)(_n3##x,_p1##y,z,v)), \ (I[27] = (img)(_n3##x,y,z,v)), \ (I[34] = (img)(_n3##x,_n1##y,z,v)), \ (I[41] = (img)(_n3##x,_n2##y,z,v)), \ (I[48] = (img)(_n3##x,_n3##y,z,v)),1)) || \ _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \ I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \ I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \ I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \ I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \ I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \ I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \ I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \ _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x) #define cimg_for2x2x2(img,x,y,z,v,I) \ cimg_for2((img).depth,z) cimg_for2((img).height,y) for (int x = 0, \ _n1##x = (int)( \ (I[0] = (img)(0,y,z,v)), \ (I[2] = (img)(0,_n1##y,z,v)), \ (I[4] = (img)(0,y,_n1##z,v)), \ (I[6] = (img)(0,_n1##y,_n1##z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)), \ (I[5] = (img)(_n1##x,y,_n1##z,v)), \ (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ ++x, ++_n1##x) #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ cimg_for_in2((img).depth,z0,z1,z) cimg_for_in2((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _n1##x = (int)( \ (I[0] = (img)(x,y,z,v)), \ (I[2] = (img)(x,_n1##y,z,v)), \ (I[4] = (img)(x,y,_n1##z,v)), \ (I[6] = (img)(x,_n1##y,_n1##z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[1] = (img)(_n1##x,y,z,v)), \ (I[3] = (img)(_n1##x,_n1##y,z,v)), \ (I[5] = (img)(_n1##x,y,_n1##z,v)), \ (I[7] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \ ++x, ++_n1##x) #define cimg_for3x3x3(img,x,y,z,v,I) \ cimg_for3((img).depth,z) cimg_for3((img).height,y) for (int x = 0, \ _p1##x = 0, \ _n1##x = (int)( \ (I[0] = I[1] = (img)(0,_p1##y,_p1##z,v)), \ (I[3] = I[4] = (img)(0,y,_p1##z,v)), \ (I[6] = I[7] = (img)(0,_n1##y,_p1##z,v)), \ (I[9] = I[10] = (img)(0,_p1##y,z,v)), \ (I[12] = I[13] = (img)(0,y,z,v)), \ (I[15] = I[16] = (img)(0,_n1##y,z,v)), \ (I[18] = I[19] = (img)(0,_p1##y,_n1##z,v)), \ (I[21] = I[22] = (img)(0,y,_n1##z,v)), \ (I[24] = I[25] = (img)(0,_n1##y,_n1##z,v)), \ 1>=(img).width?(int)((img).width)-1:1); \ (_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ (I[5] = (img)(_n1##x,y,_p1##z,v)), \ (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ (I[11] = (img)(_n1##x,_p1##y,z,v)), \ (I[14] = (img)(_n1##x,y,z,v)), \ (I[17] = (img)(_n1##x,_n1##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ (I[23] = (img)(_n1##x,y,_n1##z,v)), \ (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x; \ I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ _p1##x = x++, ++_n1##x) #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,v,I) \ cimg_for_in3((img).depth,z0,z1,z) cimg_for_in3((img).height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \ _p1##x = x-1<0?0:x-1, \ _n1##x = (int)( \ (I[0] = (img)(_p1##x,_p1##y,_p1##z,v)), \ (I[3] = (img)(_p1##x,y,_p1##z,v)), \ (I[6] = (img)(_p1##x,_n1##y,_p1##z,v)), \ (I[9] = (img)(_p1##x,_p1##y,z,v)), \ (I[12] = (img)(_p1##x,y,z,v)), \ (I[15] = (img)(_p1##x,_n1##y,z,v)), \ (I[18] = (img)(_p1##x,_p1##y,_n1##z,v)), \ (I[21] = (img)(_p1##x,y,_n1##z,v)), \ (I[24] = (img)(_p1##x,_n1##y,_n1##z,v)), \ (I[1] = (img)(x,_p1##y,_p1##z,v)), \ (I[4] = (img)(x,y,_p1##z,v)), \ (I[7] = (img)(x,_n1##y,_p1##z,v)), \ (I[10] = (img)(x,_p1##y,z,v)), \ (I[13] = (img)(x,y,z,v)), \ (I[16] = (img)(x,_n1##y,z,v)), \ (I[19] = (img)(x,_p1##y,_n1##z,v)), \ (I[22] = (img)(x,y,_n1##z,v)), \ (I[25] = (img)(x,_n1##y,_n1##z,v)), \ x+1>=(int)(img).width?(int)((img).width)-1:x+1); \ x<=(int)(x1) && ((_n1##x<(int)((img).width) && ( \ (I[2] = (img)(_n1##x,_p1##y,_p1##z,v)), \ (I[5] = (img)(_n1##x,y,_p1##z,v)), \ (I[8] = (img)(_n1##x,_n1##y,_p1##z,v)), \ (I[11] = (img)(_n1##x,_p1##y,z,v)), \ (I[14] = (img)(_n1##x,y,z,v)), \ (I[17] = (img)(_n1##x,_n1##y,z,v)), \ (I[20] = (img)(_n1##x,_p1##y,_n1##z,v)), \ (I[23] = (img)(_n1##x,y,_n1##z,v)), \ (I[26] = (img)(_n1##x,_n1##y,_n1##z,v)),1)) || \ x==--_n1##x); \ I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \ I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \ I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \ _p1##x = x++, ++_n1##x) #define _CImg_stdarg(img,a0,a1,N,t) \ { unsigned int _siz = (unsigned int)N; \ if (_siz--) { \ va_list ap; \ va_start(ap,a1); \ T *ptrd = (img).data; \ *(ptrd++) = (T)a0; \ if (_siz--) { \ *(ptrd++) = (T)a1; \ for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \ } \ va_end(ap); \ }} /* #------------------------------------------------ # # # Definition of the cimg_library:: namespace # # #------------------------------------------------ */ //! Namespace that encompasses all classes and functions of the %CImg library. /** This namespace is defined to avoid functions and class names collisions that could happen with the include of other C++ header files. Anyway, it should not happen often and you should start most of your %CImg-based programs with \code #include "CImg.h" using namespace cimg_library; \endcode to simplify the declaration of %CImg Library objects variables afterwards. **/ namespace cimg_library { // Define the classes of the CImg Library. template struct CImg; template struct CImgList; struct CImgDisplay; struct CImgException; namespace cimg { // The bodies of the functions below are defined afterwards inline void info(); inline unsigned int& exception_mode(); inline int dialog(const char *title, const char *msg, const char *button1_txt="OK", const char *button2_txt=0, const char *button3_txt=0, const char *button4_txt=0, const char *button5_txt=0, const char *button6_txt=0, const bool centering=false); template inline void marching_cubes(const tfunc& func, const float isovalue, const float x0, const float y0, const float z0, const float x1, const float y1, const float z1, const float resx, const float resy, const float resz, CImgList& points, CImgList& primitives, const bool invert_faces=false); template inline void marching_squares(const tfunc& func, const float isovalue, const float x0, const float y0, const float x1, const float y1, const float resx, const float resy, CImgList& points, CImgList& primitives); } /* #---------------------------------------------- # # # Definition of the CImgException structures # # #---------------------------------------------- */ // Never use the following macro in your own code ! #define cimg_exception_err(etype,disp_flag) \ std::va_list ap; \ va_start(ap,format); \ std::vsprintf(message,format,ap); \ va_end(ap); \ if (cimg::exception_mode()>=1) { \ if (cimg::exception_mode()>=2 && disp_flag) { \ try { cimg::dialog(etype,message,"Abort"); } \ catch (CImgException&) { std::fprintf(stderr,"\n# %s :\n%s\n\n",etype,message); } \ } else std::fprintf(stderr,"\n# %s :\n%s\n\n",etype,message); \ } \ if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \ //! Class which is thrown when an error occured during a %CImg library function call. /** \section ex1 Overview CImgException is the base class of %CImg exceptions. Exceptions are thrown by the %CImg Library when an error occured in a %CImg library function call. CImgException is seldom thrown itself. Children classes that specify the kind of error encountered are generally used instead. These sub-classes are : - \b CImgInstanceException : Thrown when the instance associated to the called %CImg function is not correctly defined. Generally, this exception is thrown when one tries to process \a empty images. The example below will throw a \a CImgInstanceException. \code CImg img; // Construct an empty image. img.blur(10); // Try to blur the image. \endcode - \b CImgArgumentException : Thrown when one of the arguments given to the called %CImg function is not correct. Generally, this exception is thrown when arguments passed to the function are outside an admissible range of values. The example below will throw a \a CImgArgumentException. \code CImg img(100,100,1,3); // Define a 100x100 color image with float pixels. img = 0; // Try to fill pixels from the 0 pointer (invalid argument to operator=() ). \endcode - \b CImgIOException : Thrown when an error occured when trying to load or save image files. The example below will throw a \a CImgIOException. \code CImg img("file_doesnt_exist.jpg"); // Try to load a file that doesn't exist. \endcode - \b CImgDisplayException : Thrown when an error occured when trying to display an image in a window. This exception is thrown when image display request cannot be satisfied. The parent class CImgException may be thrown itself when errors that cannot be classified in one of the above type occur. It is recommended not to throw CImgExceptions yourself, since there are normally reserved to %CImg Library functions. \b CImgInstanceException, \b CImgArgumentException, \b CImgIOException and \b CImgDisplayException are simple subclasses of CImgException and are thus not detailled more in this reference documentation. \section ex2 Exception handling When an error occurs, the %CImg Library first displays the error in a modal window. Then, it throws an instance of the corresponding exception class, generally leading the program to stop (this is the default behavior). You can bypass this default behavior by handling the exceptions yourself, using a code block try { ... } catch() { ... }. In this case, you can avoid the apparition of the modal window, by defining the environment variable cimg_debug to 0 before including the %CImg header file. The example below shows how to cleanly handle %CImg Library exceptions : \code #define cimg_debug 0 // Disable modal window in CImg exceptions. #define "CImg.h" int main() { try { ...; // Here, do what you want. } catch (CImgInstanceException &e) { std::fprintf(stderr,"CImg Library Error : %s",e.message); // Display your own error message ... // Do what you want now. } } \endcode **/ struct CImgException { char message[1024]; //!< Message associated with the error that thrown the exception. CImgException() { message[0]='\0'; } CImgException(const char *format,...) { cimg_exception_err("CImgException",true); } }; // The \ref CImgInstanceException class is used to throw an exception related // to a non suitable instance encountered in a library function call. struct CImgInstanceException : public CImgException { CImgInstanceException(const char *format,...) { cimg_exception_err("CImgInstanceException",true); } }; // The \ref CImgArgumentException class is used to throw an exception related // to invalid arguments encountered in a library function call. struct CImgArgumentException : public CImgException { CImgArgumentException(const char *format,...) { cimg_exception_err("CImgArgumentException",true); } }; // The \ref CImgIOException class is used to throw an exception related // to Input/Output file problems encountered in a library function call. struct CImgIOException : public CImgException { CImgIOException(const char *format,...) { cimg_exception_err("CImgIOException",true); } }; // The CImgDisplayException class is used to throw an exception related to display problems // encountered in a library function call. struct CImgDisplayException : public CImgException { CImgDisplayException(const char *format,...) { cimg_exception_err("CImgDisplayException",false); } }; /* #------------------------------------- # # # Definition of the namespace 'cimg' # # #------------------------------------- */ //! Namespace that encompasses \a low-level functions and variables of the %CImg Library. /** Most of the functions and variables within this namespace are used by the library for low-level processing. Nevertheless, documented variables and functions of this namespace may be used safely in your own source code. \warning Never write using namespace cimg_library::cimg; in your source code, since a lot of functions of the cimg:: namespace have prototypes similar to standard C functions defined in the global namespace ::. **/ namespace cimg { // Define the traits that will be used to determine the best data type to work with. template struct type { static const char* string() { static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24", "unknown32", "unknown40", "unknown48", "unknown56", "unknown64", "unknown72", "unknown80", "unknown88", "unknown96", "unknown104", "unknown112", "unknown120", "unknown128" }; return s[(sizeof(T)<17)?sizeof(T):0]; } static bool is_float() { return false; } static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); } static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); } static const char* format() { return "%s"; } static const char* format(const T& val) { static const char *s = "unknown"; return s; } }; template<> struct type { static const char* string() { static const char *const s = "bool"; return s; } static bool is_float() { return false; } static bool min() { return false; } static bool max() { return true; } static const char* format() { return "%s"; } static const char* format(const bool& val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned char"; return s; } static bool is_float() { return false; } static unsigned char min() { return 0; } static unsigned char max() { return (unsigned char)~0U; } static const char* format() { return "%u"; } static const unsigned int format(const unsigned char& val) { return (unsigned int)val; } }; template<> struct type { static const char* string() { static const char *const s = "char"; return s; } static bool is_float() { return false; } static char min() { return (char)(-1L<<(8*sizeof(char)-1)); } static char max() { return ~((char)(-1L<<(8*sizeof(char)-1))); } static const char* format() { return "%d"; } static const int format(const char& val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned short"; return s; } static bool is_float() { return false; } static unsigned short min() { return 0; } static unsigned short max() { return (unsigned short)~0U; } static const char* format() { return "%u"; } static const unsigned int format(const unsigned short& val) { return (unsigned int)val; } }; template<> struct type { static const char* string() { static const char *const s = "short"; return s; } static bool is_float() { return false; } static short min() { return (short)(-1L<<(8*sizeof(short)-1)); } static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); } static const char* format() { return "%d"; } static const int format(const short& val) { return (int)val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned int"; return s; } static bool is_float() { return false; } static unsigned int min() { return 0; } static unsigned int max() { return (unsigned int)~0U; } static const char* format() { return "%u"; } static const unsigned int format(const unsigned int& val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "int"; return s; } static bool is_float() { return false; } static int min() { return (int)(-1L<<(8*sizeof(int)-1)); } static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); } static const char* format() { return "%d"; } static const int format(const int& val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "unsigned long"; return s; } static bool is_float() { return false; } static unsigned long min() { return 0; } static unsigned long max() { return (unsigned long)~0UL; } static const char* format() { return "%lu"; } static const unsigned long format(const unsigned long& val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "long"; return s; } static bool is_float() { return false; } static long min() { return (long)(-1L<<(8*sizeof(long)-1)); } static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); } static const char* format() { return "%ld"; } static const long format(const long& val) { return val; } }; template<> struct type { static const char* string() { static const char *const s = "float"; return s; } static bool is_float() { return true; } static float min() { return -3.4E38f; } static float max() { return 3.4E38f; } static const char* format() { return "%g"; } static const double format(const float& val) { return (double)val; } }; template<> struct type { static const char* string() { static const char *const s = "double"; return s; } static bool is_float() { return true; } static double min() { return -1.7E308; } static double max() { return 1.7E308; } static const char* format() { return "%g"; } static const double format(const double& val) { return val; } }; template struct superset { typedef T type; }; template<> struct superset { typedef unsigned char type; }; template<> struct superset { typedef char type; }; template<> struct superset { typedef unsigned short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned short type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef short type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned int type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef int type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef unsigned long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef long type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef float type; }; template<> struct superset { typedef double type; }; template<> struct superset { typedef double type; }; template struct superset2 { typedef typename superset::type>::type type; }; template struct last { typedef t2 type; }; // Define internal library variables. #if cimg_display_type==1 struct X11info { volatile unsigned int nb_wins; pthread_mutex_t* mutex; pthread_t* event_thread; CImgDisplay* wins[1024]; Display* display; unsigned int nb_bits; GC* gc; bool blue_first; bool byte_order; bool shm_enabled; #ifdef cimg_use_xrandr XRRScreenSize *resolutions; Rotation curr_rotation; unsigned int curr_resolution; unsigned int nb_resolutions; #endif X11info():nb_wins(0),mutex(0),event_thread(0),display(0), nb_bits(0),gc(0),blue_first(false),byte_order(false),shm_enabled(false) { #ifdef cimg_use_xrandr resolutions = 0; curr_rotation = 0; curr_resolution = nb_resolutions = 0; #endif } }; #if defined(cimg_module) X11info& X11attr(); #elif defined(cimg_main) X11info& X11attr() { static X11info val; return val; } #else inline X11info& X11attr() { static X11info val; return val; } #endif #elif cimg_display_type==2 struct Win32info { HANDLE wait_event; Win32info() { wait_event = CreateEvent(0,FALSE,FALSE,0); } }; #if defined(cimg_module) Win32info& Win32attr(); #elif defined(cimg_main) Win32info& Win32attr() { static Win32info val; return val; } #else inline Win32info& Win32attr() { static Win32info val; return val; } #endif #endif inline unsigned int& exception_mode() { static unsigned int mode = cimg_debug; return mode; } #ifdef cimg_color_terminal const char t_normal[] = { 0x1b,'[','0',';','0',';','0','m','\0' }; const char t_red[] = { 0x1b,'[','4',';','3','1',';','5','9','m','\0' }; const char t_bold[] = { 0x1b,'[','1','m','\0' }; const char t_purple[] = { 0x1b,'[','0',';','3','5',';','5','9','m','\0' }; #else const char t_normal[] = { '\0' }; const char *const t_red = cimg::t_normal, *const t_bold = cimg::t_normal, *const t_purple = cimg::t_normal; #endif #if cimg_display_type==1 // Keycodes for X11-based graphical systems const unsigned int keyESC = XK_Escape; const unsigned int keyF1 = XK_F1; const unsigned int keyF2 = XK_F2; const unsigned int keyF3 = XK_F3; const unsigned int keyF4 = XK_F4; const unsigned int keyF5 = XK_F5; const unsigned int keyF6 = XK_F6; const unsigned int keyF7 = XK_F7; const unsigned int keyF8 = XK_F8; const unsigned int keyF9 = XK_F9; const unsigned int keyF10 = XK_F10; const unsigned int keyF11 = XK_F11; const unsigned int keyF12 = XK_F12; const unsigned int keyPAUSE = XK_Pause; const unsigned int key1 = XK_1; const unsigned int key2 = XK_2; const unsigned int key3 = XK_3; const unsigned int key4 = XK_4; const unsigned int key5 = XK_5; const unsigned int key6 = XK_6; const unsigned int key7 = XK_7; const unsigned int key8 = XK_8; const unsigned int key9 = XK_9; const unsigned int key0 = XK_0; const unsigned int keyBACKSPACE = XK_BackSpace; const unsigned int keyINSERT = XK_Insert; const unsigned int keyHOME = XK_Home; const unsigned int keyPAGEUP = XK_Page_Up; const unsigned int keyTAB = XK_Tab; const unsigned int keyQ = XK_q; const unsigned int keyW = XK_w; const unsigned int keyE = XK_e; const unsigned int keyR = XK_r; const unsigned int keyT = XK_t; const unsigned int keyY = XK_y; const unsigned int keyU = XK_u; const unsigned int keyI = XK_i; const unsigned int keyO = XK_o; const unsigned int keyP = XK_p; const unsigned int keyDELETE = XK_Delete; const unsigned int keyEND = XK_End; const unsigned int keyPAGEDOWN = XK_Page_Down; const unsigned int keyCAPSLOCK = XK_Caps_Lock; const unsigned int keyA = XK_a; const unsigned int keyS = XK_s; const unsigned int keyD = XK_d; const unsigned int keyF = XK_f; const unsigned int keyG = XK_g; const unsigned int keyH = XK_h; const unsigned int keyJ = XK_j; const unsigned int keyK = XK_k; const unsigned int keyL = XK_l; const unsigned int keyENTER = XK_Return; const unsigned int keySHIFTLEFT = XK_Shift_L; const unsigned int keyZ = XK_z; const unsigned int keyX = XK_x; const unsigned int keyC = XK_c; const unsigned int keyV = XK_v; const unsigned int keyB = XK_b; const unsigned int keyN = XK_n; const unsigned int keyM = XK_m; const unsigned int keySHIFTRIGHT = XK_Shift_R; const unsigned int keyARROWUP = XK_Up; const unsigned int keyCTRLLEFT = XK_Control_L; const unsigned int keyAPPLEFT = XK_Super_L; const unsigned int keySPACE = XK_space; const unsigned int keyALTGR = XK_Alt_R; const unsigned int keyAPPRIGHT = XK_Super_R; const unsigned int keyMENU = XK_Menu; const unsigned int keyCTRLRIGHT = XK_Control_R; const unsigned int keyARROWLEFT = XK_Left; const unsigned int keyARROWDOWN = XK_Down; const unsigned int keyARROWRIGHT = XK_Right; const unsigned int keyPAD0 = XK_KP_0; const unsigned int keyPAD1 = XK_KP_1; const unsigned int keyPAD2 = XK_KP_2; const unsigned int keyPAD3 = XK_KP_3; const unsigned int keyPAD4 = XK_KP_4; const unsigned int keyPAD5 = XK_KP_5; const unsigned int keyPAD6 = XK_KP_6; const unsigned int keyPAD7 = XK_KP_7; const unsigned int keyPAD8 = XK_KP_8; const unsigned int keyPAD9 = XK_KP_9; const unsigned int keyPADADD = XK_KP_Add; const unsigned int keyPADSUB = XK_KP_Subtract; const unsigned int keyPADMUL = XK_KP_Multiply; const unsigned int keyPADDIV = XK_KP_Divide; #elif (cimg_display_type==2 && cimg_OS==2) // Keycodes for Windows-OS const unsigned int keyESC = VK_ESCAPE; const unsigned int keyF1 = VK_F1; const unsigned int keyF2 = VK_F2; const unsigned int keyF3 = VK_F3; const unsigned int keyF4 = VK_F4; const unsigned int keyF5 = VK_F5; const unsigned int keyF6 = VK_F6; const unsigned int keyF7 = VK_F7; const unsigned int keyF8 = VK_F8; const unsigned int keyF9 = VK_F9; const unsigned int keyF10 = VK_F10; const unsigned int keyF11 = VK_F11; const unsigned int keyF12 = VK_F12; const unsigned int keyPAUSE = VK_PAUSE; const unsigned int key1 = '1'; const unsigned int key2 = '2'; const unsigned int key3 = '3'; const unsigned int key4 = '4'; const unsigned int key5 = '5'; const unsigned int key6 = '6'; const unsigned int key7 = '7'; const unsigned int key8 = '8'; const unsigned int key9 = '9'; const unsigned int key0 = '0'; const unsigned int keyBACKSPACE = VK_BACK; const unsigned int keyINSERT = VK_INSERT; const unsigned int keyHOME = VK_HOME; const unsigned int keyPAGEUP = VK_PRIOR; const unsigned int keyTAB = VK_TAB; const unsigned int keyQ = 'Q'; const unsigned int keyW = 'W'; const unsigned int keyE = 'E'; const unsigned int keyR = 'R'; const unsigned int keyT = 'T'; const unsigned int keyY = 'Y'; const unsigned int keyU = 'U'; const unsigned int keyI = 'I'; const unsigned int keyO = 'O'; const unsigned int keyP = 'P'; const unsigned int keyDELETE = VK_DELETE; const unsigned int keyEND = VK_END; const unsigned int keyPAGEDOWN = VK_NEXT; const unsigned int keyCAPSLOCK = VK_CAPITAL; const unsigned int keyA = 'A'; const unsigned int keyS = 'S'; const unsigned int keyD = 'D'; const unsigned int keyF = 'F'; const unsigned int keyG = 'G'; const unsigned int keyH = 'H'; const unsigned int keyJ = 'J'; const unsigned int keyK = 'K'; const unsigned int keyL = 'L'; const unsigned int keyENTER = VK_RETURN; const unsigned int keySHIFTLEFT = VK_SHIFT; const unsigned int keyZ = 'Z'; const unsigned int keyX = 'X'; const unsigned int keyC = 'C'; const unsigned int keyV = 'V'; const unsigned int keyB = 'B'; const unsigned int keyN = 'N'; const unsigned int keyM = 'M'; const unsigned int keySHIFTRIGHT = VK_SHIFT; const unsigned int keyARROWUP = VK_UP; const unsigned int keyCTRLLEFT = VK_CONTROL; const unsigned int keyAPPLEFT = VK_LWIN; const unsigned int keySPACE = VK_SPACE; const unsigned int keyALTGR = VK_CONTROL; const unsigned int keyAPPRIGHT = VK_RWIN; const unsigned int keyMENU = VK_APPS; const unsigned int keyCTRLRIGHT = VK_CONTROL; const unsigned int keyARROWLEFT = VK_LEFT; const unsigned int keyARROWDOWN = VK_DOWN; const unsigned int keyARROWRIGHT = VK_RIGHT; const unsigned int keyPAD0 = 0x60; const unsigned int keyPAD1 = 0x61; const unsigned int keyPAD2 = 0x62; const unsigned int keyPAD3 = 0x63; const unsigned int keyPAD4 = 0x64; const unsigned int keyPAD5 = 0x65; const unsigned int keyPAD6 = 0x66; const unsigned int keyPAD7 = 0x67; const unsigned int keyPAD8 = 0x68; const unsigned int keyPAD9 = 0x69; const unsigned int keyPADADD = VK_ADD; const unsigned int keyPADSUB = VK_SUBTRACT; const unsigned int keyPADMUL = VK_MULTIPLY; const unsigned int keyPADDIV = VK_DIVIDE; #else // Define unknow keycodes when no display const unsigned int keyESC = 1U; const unsigned int keyF1 = 2U; const unsigned int keyF2 = 3U; const unsigned int keyF3 = 4U; const unsigned int keyF4 = 5U; const unsigned int keyF5 = 6U; const unsigned int keyF6 = 7U; const unsigned int keyF7 = 8U; const unsigned int keyF8 = 9U; const unsigned int keyF9 = 10U; const unsigned int keyF10 = 11U; const unsigned int keyF11 = 12U; const unsigned int keyF12 = 13U; const unsigned int keyPAUSE = 14U; const unsigned int key1 = 15U; const unsigned int key2 = 16U; const unsigned int key3 = 17U; const unsigned int key4 = 18U; const unsigned int key5 = 19U; const unsigned int key6 = 20U; const unsigned int key7 = 21U; const unsigned int key8 = 22U; const unsigned int key9 = 23U; const unsigned int key0 = 24U; const unsigned int keyBACKSPACE = 25U; const unsigned int keyINSERT = 26U; const unsigned int keyHOME = 27U; const unsigned int keyPAGEUP = 28U; const unsigned int keyTAB = 29U; const unsigned int keyQ = 30U; const unsigned int keyW = 31U; const unsigned int keyE = 32U; const unsigned int keyR = 33U; const unsigned int keyT = 34U; const unsigned int keyY = 35U; const unsigned int keyU = 36U; const unsigned int keyI = 37U; const unsigned int keyO = 38U; const unsigned int keyP = 39U; const unsigned int keyDELETE = 40U; const unsigned int keyEND = 41U; const unsigned int keyPAGEDOWN = 42U; const unsigned int keyCAPSLOCK = 43U; const unsigned int keyA = 44U; const unsigned int keyS = 45U; const unsigned int keyD = 46U; const unsigned int keyF = 47U; const unsigned int keyG = 48U; const unsigned int keyH = 49U; const unsigned int keyJ = 50U; const unsigned int keyK = 51U; const unsigned int keyL = 52U; const unsigned int keyENTER = 53U; const unsigned int keySHIFTLEFT = 54U; const unsigned int keyZ = 55U; const unsigned int keyX = 56U; const unsigned int keyC = 57U; const unsigned int keyV = 58U; const unsigned int keyB = 59U; const unsigned int keyN = 60U; const unsigned int keyM = 61U; const unsigned int keySHIFTRIGHT = 62U; const unsigned int keyARROWUP = 63U; const unsigned int keyCTRLLEFT = 64U; const unsigned int keyAPPLEFT = 65U; const unsigned int keySPACE = 66U; const unsigned int keyALTGR = 67U; const unsigned int keyAPPRIGHT = 68U; const unsigned int keyMENU = 69U; const unsigned int keyCTRLRIGHT = 70U; const unsigned int keyARROWLEFT = 71U; const unsigned int keyARROWDOWN = 72U; const unsigned int keyARROWRIGHT = 73U; const unsigned int keyPAD0 = 74U; const unsigned int keyPAD1 = 75U; const unsigned int keyPAD2 = 76U; const unsigned int keyPAD3 = 77U; const unsigned int keyPAD4 = 78U; const unsigned int keyPAD5 = 79U; const unsigned int keyPAD6 = 80U; const unsigned int keyPAD7 = 81U; const unsigned int keyPAD8 = 82U; const unsigned int keyPAD9 = 83U; const unsigned int keyPADADD = 84U; const unsigned int keyPADSUB = 85U; const unsigned int keyPADMUL = 86U; const unsigned int keyPADDIV = 87U; #endif const double PI = 3.14159265358979323846; //!< Definition of the mathematical constant PI // Definition of a 7x11 font, used to return a default font for drawing text. const unsigned int font7x11[7*11*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x90,0x0,0x7f0000,0x40000,0x0,0x0,0x4010c0a4,0x82000040,0x11848402,0x18480050,0x80430292,0x8023,0x9008000, 0x40218140,0x4000040,0x21800402,0x18000051,0x1060500,0x8083,0x10000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x24002,0x4031,0x80000000,0x10000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c0400,0x40020000,0x80070080,0x40440e00,0x0,0x0,0x1,0x88180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x200000,0x0,0x0,0x80000,0x0,0x0,0x20212140,0x5000020,0x22400204,0x240000a0,0x40848500,0x4044,0x80010038,0x20424285,0xa000020, 0x42428204,0x2428e0a0,0x82090a14,0x4104,0x85022014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10240a7,0x88484040,0x40800000,0x270c3,0x87811e0e, 0x7c70e000,0x78,0x3c23c1ef,0x1f3e1e89,0xf1c44819,0xa23cf0f3,0xc3cff120,0xc18307f4,0x4040400,0x20000,0x80080080,0x40200,0x0, 0x40000,0x2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8188,0x50603800,0xf3c00000,0x1c004003,0xc700003e,0x18180,0xc993880,0x10204081, 0x2071ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x7d1224,0x48906048,0x0,0x4000000,0x0,0x9000,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x10240aa,0x14944080,0x23610000,0x68940,0x40831010,0x8891306,0x802044,0x44522208,0x90202088,0x40448819,0xb242890a,0x24011111, 0x49448814,0x4040a00,0xe2c3c7,0x8e3f3cb9,0xc1c44216,0xee38b0f2,0xe78f9120,0xc18507e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x101c207,0x88a04001,0x9c00000,0x2200a041,0x8200113a,0x8240,0x50a3110,0x2850a142,0x850c2081,0x2040204,0x8104592,0x142850a1, 0x42cd1224,0x4888bc48,0x70e1c387,0xe3b3c70,0xe1c38e1c,0x38707171,0xc3870e1c,0x10791224,0x48906c41,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x10003ee,0x15140080,0x21810000,0x48840,0x40851020,0x8911306,0x31fd804,0x9c522408,0x90204088,0x4045081a,0xba42890a,0x24011111, 0x49285024,0x2041b00,0x132408,0x910844c8,0x4044821b,0x7244c913,0x24041111,0x49488822,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x28204,0x85006001,0x6a414000,0x3a004043,0xc700113a,0x8245,0x50a3a00,0x2850a142,0x850c4081,0x2040204,0x81045d2,0x142850a1, 0x24951224,0x48852250,0x8102040,0x81054089,0x12244204,0x8108992,0x24489122,0x991224,0x4888b222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1000143,0xa988080,0x2147c01f,0x88840,0x83091c2c,0x1070f000,0xc000608,0xa48bc408,0x9e3c46f8,0x40460816,0xaa42f10b,0xc3811111, 0x35102044,0x1041100,0xf22408,0x9f084488,0x40470212,0x62448912,0x6041111,0x55308846,0x8061c80,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1028704,0x8f805801,0x4be28fdf,0x220001f0,0x111a,0x60000182,0x82c5c710,0x44891224,0x489640f1,0xe3c78204,0x810e552,0x142850a1, 0x18a51224,0x48822250,0x78f1e3c7,0x8f1f40f9,0xf3e7c204,0x8108912,0x24489122,0x7ea91224,0x4888a222,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x10007e2,0x85648080,0x20010000,0x88841,0x8f8232,0x20881000,0xc1fc610,0xbefa2408,0x90204288,0x40450816,0xa642810a,0x4041110a, 0x36282084,0x1042080,0x1122408,0x90084488,0x40450212,0x62448912,0x184110a,0x55305082,0x8042700,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1028207,0x82004801,0x68050040,0x1c000040,0x110a,0x60000001,0x45484d10,0x7cf9f3e7,0xcf944081,0x2040204,0x8104532,0x142850a1, 0x18a51224,0x48822248,0x89122448,0x91244081,0x2040204,0x8108912,0x24489122,0xc91224,0x48852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x282, 0x89630080,0x20010c00,0x30108842,0x810222,0x20882306,0x3001800,0x408a2208,0x90202288,0x40448814,0xa642810a,0x2041110a,0x26442104, 0x840000,0x1122408,0x90084488,0x40448212,0x62448912,0x84130a,0x36485102,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x101c208,0x4f802801, 0x8028040,0x40,0x130a,0x2,0x85e897a0,0x44891224,0x489c2081,0x2040204,0x8104532,0x142850a1,0x24cd1224,0x48823c44,0x89122448, 0x91244081,0x2040204,0x8108912,0x24489122,0xc93264,0xc9852214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100028f,0x109f0080,0x20010c00, 0x303071f3,0xc7011c1c,0x4071c306,0x802010,0x3907c1ef,0x1f201e89,0xf3844f90,0xa23c80f2,0x17810e04,0x228223f4,0x840000,0xfbc3c7, 0x8f083c88,0x40444212,0x6238f0f2,0x7039d04,0x228423e2,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1008780,0x2201800,0xf0014000,0x1f0, 0x1d0a,0x5,0x851e140,0x83060c18,0x30671ef9,0xf3e7cf9f,0x3e7c7911,0xe3c78f1e,0x42f8e1c3,0x8702205c,0x7cf9f3e7,0xcf9b3c78,0xf1e3c204, 0x8107111,0xc3870e1c,0x10f1d3a7,0x4e823c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x2,0x40,0x40000400,0x200000,0x0,0x2,0x0,0x0,0x0,0x0,0x18, 0x0,0x4,0x44007f,0x0,0x400,0x400000,0x8010,0x0,0x6002,0x8040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x200800,0x0,0x0,0x100a, 0x400000,0x44,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x62018,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31,0x80000800, 0x400000,0x0,0x4,0x0,0x0,0x0,0x0,0xc,0x0,0x7,0x3c0000,0x0,0x3800,0x3800000,0x8010,0x0,0x1c001,0x881c0000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x207000,0x0,0x0,0x100a,0xc00000,0x3c,0x0,0xc00,0x0,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x0,0x1c2070 }; // Definition of a 10x13 font (used in dialog boxes). const unsigned int font10x13[256*10*13/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0, 0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120, 0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001, 0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801, 0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0, 0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010, 0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480, 0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140, 0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010, 0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008, 0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006, 0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088, 0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000, 0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220, 0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208, 0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040, 0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508, 0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018, 0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220, 0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484, 0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808, 0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264, 0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010, 0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100, 0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800, 0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044, 0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822, 0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200, 0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000, 0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8, 0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008, 0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000, 0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220, 0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402, 0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980, 0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421, 0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020, 0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701, 0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0, 0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c, 0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0, 0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000, 0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000, 0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000, 0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0, 0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040 }; // Definition of a 8x17 font const unsigned int font8x17[8*17*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x2400,0x2400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20081834,0x1c0000,0x20081800,0x20081800,0x342008, 0x18340000,0x200818,0x80000,0x0,0x180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4200000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x380000,0x4000,0x2000c00,0x40100840,0x70000000,0x0,0x0,0x1c,0x10700000,0x7,0x0, 0x1800,0x1800,0x0,0x0,0x0,0x14,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1010242c,0x14140000,0x10102414,0x10102414,0x2c1010,0x242c1400, 0x101024,0x14100038,0x0,0x240000,0x0,0x0,0x30000000,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x12,0x0,0x8100000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x80000,0x10004000,0x2001000,0x40000040,0x10000000,0x0,0x0,0x10,0x10100000,0x4, 0x0,0x18000000,0x0,0x0,0x0,0x34002400,0x2400,0x0,0x0,0x0,0x3c,0x0,0x8000000,0x0,0x60607800,0x0,0x140000,0x0,0x0,0x0,0x0,0x0, 0x44,0x10081834,0x240000,0x10081800,0x10081800,0x1c341008,0x18340000,0x100818,0x84000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102812, 0x8601c10,0x8100800,0x2,0x1c383e3e,0x67e1e7f,0x3e3c0000,0x38,0x1e087e1e,0x7c7f7f1e,0x417c1c42,0x4063611c,0x7e1c7e3e,0xfe414181, 0x63827f10,0x40081000,0x8004000,0x2001000,0x40000040,0x10000000,0x0,0x10000000,0x10,0x10100000,0x3c000008,0x0,0x24003e00, 0x3f007f00,0x0,0x0,0x2ce91800,0x1882,0x10101c,0xc2103c,0x143c3c00,0x3c00,0x18003c3c,0x10001f00,0x181c00,0x20200810,0x8080808, 0x8083e1e,0x7f7f7f7f,0x7c7c7c7c,0x7c611c1c,0x1c1c1c00,0x1e414141,0x41824044,0x810242c,0x14180000,0x8102414,0x8102414,0x382c0810, 0x242c1400,0x81024,0x14104014,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102816,0x3e902010,0x10084910,0x4,0x22084343,0xa402102,0x41620000, 0x44,0x33144121,0x42404021,0x41100444,0x40636122,0x43224361,0x10416381,0x22440310,0x20082800,0x4000,0x2001000,0x40000040, 0x10000000,0x0,0x10000000,0x10,0x10100000,0x24000008,0x0,0x606100,0x68000300,0x8106c,0x34000000,0x4f0000,0x44,0x101020,0x441040, 0x420200,0x4200,0x24000404,0x7d00,0x82200,0x20203010,0x14141414,0x14082821,0x40404040,0x10101010,0x42612222,0x22222200,0x23414141, 0x41447e48,0x0,0x0,0x0,0x0,0x4000000,0x18,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10287f,0x49902010,0x10083e10,0x4,0x41080101, 0x1a404002,0x41411818,0x1004004,0x21144140,0x41404040,0x41100448,0x40555141,0x41414140,0x10412281,0x14280610,0x20084400,0x1c7c1c, 0x3e3c7c3a,0x5c703844,0x107f5c3c,0x7c3e3c3c,0x7e424281,0x66427e10,0x10100000,0x40100008,0x1010,0xa04000,0x48100610,0x100c3024, 0x24000000,0x4f3c00,0x2c107e28,0x3820,0x42281060,0x9d1e12,0xbd00,0x24100818,0x427d00,0x82248,0x20200800,0x14141414,0x14142840, 0x40404040,0x10101010,0x41514141,0x41414142,0x43414141,0x41284350,0x1c1c1c1c,0x1c1c6c1c,0x3c3c3c3c,0x70707070,0x3c5c3c3c, 0x3c3c3c18,0x3e424242,0x42427c42,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x102824,0x48623010,0x10081c10,0x8,0x41080103,0x127c5e04, 0x41411818,0xe7f3808,0x4f144140,0x41404040,0x41100450,0x40555141,0x41414160,0x1041225a,0x1c280410,0x1008c600,0x226622,0x66661066, 0x62100848,0x10496266,0x66663242,0x10426681,0x24220260,0x100c0000,0xf8280008,0x1010,0x606000,0x48280428,0x28042014,0x48000000, 0x494200,0x52280228,0x105420,0x3cee1058,0xa12236,0xa500,0x18101004,0x427d00,0x8226c,0x76767e10,0x14141414,0x14142840,0x40404040, 0x10101010,0x41514141,0x41414124,0x45414141,0x41284150,0x22222222,0x22221222,0x66666666,0x10101010,0x66626666,0x66666600, 0x66424242,0x42226622,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100024,0x381c4900,0x10086bfe,0x8,0x4908021c,0x22036304,0x3e630000, 0x70000710,0x51227e40,0x417f7f43,0x7f100470,0x40554941,0x43417e3e,0x1041225a,0x8100810,0x10080000,0x24240,0x42421042,0x42100850, 0x10494242,0x42422040,0x1042245a,0x18240410,0x10103900,0x407c003e,0x1818,0x1c3e10,0x4f7c087c,0x7c002010,0x48000000,0x4008, 0x527c0410,0x105078,0x2410104c,0xa13e6c,0x7f00b900,0xfe3c3c,0x421d18,0x1c1c36,0x38383810,0x22222222,0x22144e40,0x7f7f7f7f, 0x10101010,0xf1494141,0x41414118,0x49414141,0x4110435c,0x2020202,0x2021240,0x42424242,0x10101010,0x42424242,0x424242ff,0x4e424242, 0x42244224,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000fe,0xe664d00,0x10080810,0x380010,0x41080c03,0x42014108,0x633d0000,0x70000710, 0x51224140,0x41404041,0x41100448,0x40494541,0x7e414203,0x1041145a,0x14101010,0x10080000,0x3e4240,0x427e1042,0x42100870,0x10494242, 0x4242203c,0x1042245a,0x18241810,0x10104600,0xf8f60008,0x1010,0x600320,0x48f610f6,0xf6000000,0x187eff,0x3c04,0x5ef61810,0x105020, 0x24fe0064,0x9d006c,0x138ad00,0x100000,0x420518,0x36,0xc0c0c020,0x22222222,0x22224840,0x40404040,0x10101010,0x41454141,0x41414118, 0x51414141,0x41107e46,0x3e3e3e3e,0x3e3e7e40,0x7e7e7e7e,0x10101010,0x42424242,0x42424200,0x5a424242,0x42244224,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x28,0x9094500,0x10080010,0x10,0x41081801,0x7f014118,0x41010000,0xe7f3800,0x513e4140,0x41404041,0x41100444, 0x40414541,0x40414101,0x10411466,0x36103010,0x8080000,0x424240,0x42401042,0x42100848,0x10494242,0x42422002,0x10423c5a,0x18142010, 0x10100000,0x407c0010,0x1010,0x260140,0x487c307c,0x7c000000,0x180000,0x202,0x507c2010,0x105020,0x3c10003c,0x423e36,0x1004200, 0x100000,0x420500,0x3e6c,0x41e0440,0x3e3e3e3e,0x3e3e7840,0x40404040,0x10101010,0x41454141,0x41414124,0x61414141,0x41104042, 0x42424242,0x42425040,0x40404040,0x10101010,0x42424242,0x42424218,0x72424242,0x42144214,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048, 0x49096200,0x8100010,0x18001810,0x22082043,0x2432310,0x61421818,0x1004010,0x4f634121,0x42404021,0x41104444,0x40414322,0x40234143, 0x10411466,0x22106010,0x8080000,0x466622,0x66621066,0x42100844,0x10494266,0x66662042,0x10461824,0x24184010,0x10100000,0x24381010, 0x34001018,0xda4320,0x68386038,0x38000000,0x0,0x4204,0x50384010,0x105420,0x4210100c,0x3c0012,0x3c00,0x0,0x460500,0x48,0xc020c44, 0x63636363,0x63228821,0x40404040,0x10101010,0x42432222,0x22222242,0x62414141,0x41104042,0x46464646,0x46465022,0x62626262, 0x10101010,0x66426666,0x66666618,0x66464646,0x46186618,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100048,0x3e063d00,0x8100000,0x18001820, 0x1c3e7f3e,0x23c1e20,0x3e3c1818,0x10,0x20417e1e,0x7c7f401e,0x417c3842,0x7f41431c,0x401e40be,0x103e0866,0x41107f10,0x4080000, 0x3a5c1c,0x3a3c103a,0x427c0842,0xe49423c,0x7c3e203c,0xe3a1824,0x66087e10,0x10100000,0x3c103010,0x245a1010,0x5a3e10,0x3f107f10, 0x10000000,0x0,0x3c08,0x2e107e10,0x1038fc,0x101004,0x0,0x0,0xfe0000,0x7f0500,0x0,0x14041438,0x41414141,0x41418e1e,0x7f7f7f7f, 0x7c7c7c7c,0x7c431c1c,0x1c1c1c00,0xbc3e3e3e,0x3e10405c,0x3a3a3a3a,0x3a3a6e1c,0x3c3c3c3c,0x7c7c7c7c,0x3c423c3c,0x3c3c3c00, 0x7c3a3a3a,0x3a087c08,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x4200000,0x10000020,0x0,0x0,0x10,0x0,0x30000000,0x0, 0x0,0x0,0x60000,0x0,0x1c,0x4380000,0x0,0x2,0x800,0x0,0x40020000,0x0,0x8000c,0x10600000,0x2010,0x48000000,0x240000,0x0,0x0, 0x0,0x0,0x0,0x1000,0x1078,0x0,0x0,0x0,0x400500,0x0,0x1e081e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x84008,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8000000,0x0,0x20000040,0x0,0x0,0x20,0x0,0x1e000000,0x0,0x0,0x0,0x20000,0x0, 0x0,0x2000000,0x0,0x26,0x800,0x0,0x40020000,0x0,0x100000,0x10000000,0x2030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x1000,0x0, 0x0,0x0,0x400000,0x8000000,0x41e0400,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0x0,0x104010,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x1c,0x7000,0x0,0x40020000,0x0,0x300000, 0x0,0xe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000,0x0,0x0,0x0,0x400000,0x38000000,0x0,0x0,0x1c,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x1c,0x0,0x0,0x0,0x0,0x0,0x304030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 10x19 font const unsigned int font10x19[10*19*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3600000,0x36000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x180181c0,0xe81b0300,0x1801,0x81c06c18,0x181c06c,0xe8180,0x181c0e81,0xb0000006,0x60701b,0x1800000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x1c000,0x0,0x0,0x0,0x0,0x6c,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xc030360,0xb81b0480,0xc03,0x3606c0c,0x303606c,0xb80c0,0x30360b81,0xb0000003,0xc0d81b,0x3000000,0x0, 0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x0,0x0,0x2200000, 0x22000,0x0,0x0,0x0,0x0,0x0,0x0,0x30000,0x0,0xe0,0x38078000,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000c080,0x480,0x3000, 0xc0800030,0xc08000,0x300,0xc080000,0xc,0x302000,0xc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x41c01,0xe020060c, 0x800000,0x4,0x1e0703e0,0xf8060fc1,0xe1fe1e07,0x80000000,0x78,0x307e0,0x3c7c1fe7,0xf83c408f,0x80f10440,0x18660878,0x7e0787e0, 0x78ff9024,0xa0140a0,0x27f83840,0x700e000,0x18000400,0x8000,0x70004002,0x410078,0x0,0x0,0x0,0x0,0x1808,0xc000000,0xf000000, 0xe000000,0x1400,0x1e0001f,0x8007f800,0x0,0x0,0x3a3b,0x61400000,0x14202,0x20000,0x38002020,0x3c1b00,0x3e00000,0xf8,0x1c0001c0, 0x78060001,0xf800000e,0x1e00020,0x8004020,0xc0300c0,0x300c0301,0xf83c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1821e0,0x781e0781,0xe0001f10, 0x24090240,0xa02400f8,0x18018140,0xe81b0480,0x1801,0x81406c18,0x181406c,0x190e8180,0x18140e81,0xb0000006,0x60501b,0x184006c, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x26042202,0x200c06,0x800000,0x8,0x210d0611,0x40e0803,0x10026188,0x40000000, 0x8c,0xf030418,0xc6431004,0xc64082,0x110840,0x18660884,0x41084410,0x8c081024,0xa012110,0x40082020,0x101b000,0xc000400,0x8000, 0x80004002,0x410008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x18800000,0x10000000,0x2200,0x2300024,0x800,0x0,0x0,0x2e13,0x60800000, 0x8104,0x20040,0x64001040,0x80401b07,0x80100000,0x1e000,0x22000020,0x40c0003,0xc8000002,0x3300020,0x8004020,0xc0300c0,0x300c0301, 0x40c64010,0x4010008,0x2008020,0x43182210,0x84210842,0x10002190,0x24090240,0x9044018c,0xc030220,0xb81b0300,0xc03,0x2206c0c, 0x302206c,0x1e0b80c0,0x30220b81,0xb0000003,0xc0881b,0x304006c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x241f2202, 0x200802,0x4900000,0x8,0x21010408,0x20a0802,0x44090,0x20000000,0x4,0x11878408,0x80411004,0x804082,0x111040,0x1ce50986,0x40986409, 0x81022,0x12012108,0x80102020,0x1031800,0x400,0x8000,0x80004000,0x10008,0x0,0x0,0x100000,0x0,0x2008,0x2000000,0x10000000, 0x10000000,0x18,0x4000044,0x1000,0x30180,0xd81b0000,0x13,0xe0000000,0x88,0x40,0x400018c0,0x80400018,0x61f00000,0x61800,0x22020020, 0x4000007,0xc8000002,0x2100020,0x8038000,0x1e0781e0,0x781e0301,0x40804010,0x4010008,0x2008020,0x41142619,0x86619866,0x18002190, 0x24090240,0x8887e104,0x0,0x0,0x0,0x0,0x0,0x2000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20120,0x2434a202, 0x200802,0x3e00000,0x10,0x40810008,0x21a0804,0x44090,0x20000000,0x80040004,0x20848409,0x409004,0x1004082,0x112040,0x14a50902, 0x40902409,0x81022,0x11321208,0x80202010,0x1060c00,0x7c5e0,0x781e8783,0xf07a5f0e,0x1c10808,0xfc5f078,0x5e07a170,0x7c7e1024, 0xa016190,0x27f82008,0x2000000,0x20000000,0x10000000,0x80200024,0x4000044,0x2000,0x18180,0xc8320000,0x12,0xa1f00037,0x7f888, 0x1e0,0x40410880,0x80600017,0xa2100000,0x5e800,0x22020040,0x38001027,0xc8000002,0x2100020,0x8004020,0x12048120,0x48120482, 0x41004010,0x4010008,0x2008020,0x40942409,0x2409024,0x9044390,0x24090240,0x88841918,0x1f07c1f0,0x7c1f07c3,0x70781e07,0x81e07838, 0xe0380e0,0x1f17c1e0,0x781e0781,0xe0001f90,0x24090240,0x9025e102,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xff241c41, 0x1001,0x1c02000,0x10,0x40810008,0x6120f85,0xe0086190,0x20c03007,0x8007800c,0x27848419,0x409004,0x1004082,0x114040,0x14a48902, 0x40902409,0x81022,0x11321205,0x602010,0x1000000,0x86610,0x84218840,0x80866182,0x411008,0x9261884,0x61086189,0x82101022,0x12012108, 0x40082008,0x2000000,0x20030000,0x20000000,0x80200024,0x4000044,0x3006030,0xc018100,0x4c260000,0x12,0x26080048,0x83000850, 0x20250,0x403e0500,0x8078002c,0x12302200,0x92400,0x1c0200c0,0x4001027,0xc8000002,0x3308820,0x8004020,0x12048120,0x48120482, 0x41004010,0x4010008,0x2008020,0x40922409,0x2409024,0x8884690,0x24090240,0x85040920,0x21886218,0x86218860,0x88842108,0x42108408, 0x2008020,0x21186210,0x84210842,0x10302190,0x24090240,0x88461084,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x4c240182, 0x80001001,0x6b02000,0x20,0x4c810010,0x78220846,0x10081e10,0x20c0301c,0x1fe0e018,0x4d8487e1,0x409fe7,0xf9007f82,0x11a040, 0x13248902,0x41102418,0xe0081022,0x11320c05,0x402008,0x1000000,0x2409,0x409020,0x81024082,0x412008,0x9240902,0x40902101,0x101022, 0x11321208,0x40102008,0x2000000,0x7e0c8000,0xfc000003,0xf0fc0018,0x43802047,0x8c8040c8,0x32008300,0x44240000,0x0,0x4000048, 0x8c801050,0x20440,0x40221dc0,0x808c0028,0x11d0667f,0x8009c400,0x1fc180,0x4001023,0xc8300002,0x1e0ccfb,0x3ec7b020,0x12048120, 0x48120482,0x79007f9f,0xe7f9fe08,0x2008020,0xf0922409,0x2409024,0x8504490,0x24090240,0x85040920,0x802008,0x2008020,0x89004090, 0x24090208,0x2008020,0x40902409,0x2409024,0x8304390,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, 0x481c0606,0xc8001001,0x802000,0x20,0x4c810020,0x4220024,0x8102108,0x60000070,0x3820,0x48884419,0x409004,0x10e4082,0x112040, 0x13244902,0x7e1027e0,0x3c081021,0x21320c02,0x802008,0x1000000,0x7e409,0x409020,0x81024082,0x414008,0x9240902,0x40902101, 0x80101022,0x11320c08,0x40202008,0x2038800,0x200bc000,0x20000000,0x80200003,0x80f04044,0xbc080bc,0x2f000200,0x0,0x0,0x6001048, 0x8bc02020,0x20441,0xf8220200,0x80820028,0x1000cc00,0x80094400,0x201e0,0x78001021,0xc830000f,0x8000663c,0xf03c0c0,0x21084210, 0x84210846,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8204890,0x24090240,0x82040930,0x1f87e1f8,0x7e1f87e0,0x89004090, 0x24090208,0x2008020,0x40902409,0x2409024,0x8004690,0x24090240,0x88440884,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000, 0x480719c4,0x48001001,0x81fc00,0x7800020,0x40810040,0x2420024,0x8104087,0xa0000070,0x3820,0x48884409,0x409004,0x1024082,0x111040, 0x13244902,0x40102410,0x2081021,0x214a1202,0x1802008,0x1000000,0x182409,0x409fe0,0x81024082,0x41a008,0x9240902,0x40902100, 0xf8101021,0x214a0c04,0x80c0c008,0x1847000,0x7c1ee000,0x20000000,0x8020000c,0x8c044,0x1ee181ee,0x7b800000,0x707,0xf3ff0000, 0x3e0084f,0x9ee0c020,0x20440,0x40221fc0,0xc2002c,0x13f11000,0x87892400,0x20000,0x1020,0x48000000,0x3f011c6,0x31cc6180,0x21084210, 0x84210844,0x41004010,0x4010008,0x2008020,0x40912409,0x2409024,0x8505090,0x24090240,0x8204191c,0x60982609,0x82609823,0xf9007f9f, 0xe7f9fe08,0x2008020,0x40902409,0x2409024,0x9fe4c90,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xfe048224, 0x28001001,0x2000,0x40,0x40810080,0x27f8024,0x8104080,0x2000001c,0x1fe0e020,0x488fc409,0x409004,0x1024082,0x110840,0x10242902, 0x40102408,0x2081021,0x214a1202,0x1002004,0x1000000,0x102409,0x409000,0x81024082,0x411008,0x9240902,0x40902100,0x6101021, 0x214a0c04,0x81002008,0x2000000,0x201dc000,0x20000000,0x80200000,0x98044,0x1dc101dc,0x77000000,0x700,0x0,0x180448,0x1dc10020, 0x20440,0x403e0200,0x620017,0xa000cc00,0x80052800,0x20000,0x1020,0x48000000,0x6606,0x206100,0x3f0fc3f0,0xfc3f0fc7,0xc1004010, 0x4010008,0x2008020,0x4090a409,0x2409024,0x8886090,0x24090240,0x8207e106,0x40902409,0x2409024,0x81004010,0x4010008,0x2008020, 0x40902409,0x2409024,0x8005890,0x24090240,0x84840848,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x98048224,0x30001001,0x2000, 0x40,0x21010100,0x2020024,0x8204080,0x40000007,0x80078000,0x48884408,0x80411004,0x824082,0x110840,0x10242986,0x40086409,0x2081021, 0xe14a2102,0x2002004,0x1000000,0x106409,0x409000,0x81024082,0x410808,0x9240902,0x40902100,0x2101021,0x214a1202,0x82002008, 0x2000000,0x300f8000,0x20000000,0x80fc001d,0xe4088044,0xf8200f8,0x3e000000,0x300,0x0,0x80c48,0xf820020,0x20640,0x40410200, 0x803c0018,0x60006600,0x61800,0x0,0x1020,0x48000000,0xcc0a,0x20a100,0x21084210,0x84210844,0x40804010,0x4010008,0x2008020, 0x4110a619,0x86619866,0x19046110,0x24090240,0x82040102,0x41906419,0x6419064,0x81004010,0x4010008,0x2008020,0x40902409,0x2409024, 0x8307090,0x24090240,0x82840828,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x90248222,0x30000802,0x200c,0xc080,0x21010301, 0x4021042,0x10202108,0xc0c03000,0x80040020,0x4d902418,0xc6431004,0xc24082,0x6210440,0x10241884,0x40084409,0x86080840,0xc0842102, 0x4002002,0x1000000,0x18e610,0x84218820,0x80864082,0x410408,0x9240884,0x61086101,0x6101860,0xc0842103,0x4002008,0x2000000, 0x10850180,0x20330000,0x80200013,0x26184024,0x5040050,0x14000000,0x0,0x0,0x4180848,0x85040020,0x20350,0x40000200,0x800c0007, 0x80002200,0x1e000,0x0,0x1860,0x48000000,0x880a,0x40a188,0x40902409,0x2409028,0x40c64010,0x4010008,0x2008020,0x43106210,0x84210842, 0x10006108,0x42108421,0x2040102,0x6398e639,0x8e6398e4,0x88842088,0x22088208,0x2008020,0x21102210,0x84210842,0x10306118,0x66198661, 0x83061030,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0x901f01c1,0xe8000802,0xc,0xc080,0x1e07c7f8,0xf8020f81,0xe0401e07, 0x80c03000,0x20,0x279027e0,0x3c7c1fe4,0x3c408f,0x83c1027f,0x90241878,0x4007c404,0xf8080780,0xc0844082,0x7f82002,0x1000000, 0xfa5e0,0x781e87c0,0x807a409f,0xc0410207,0x9240878,0x5e07a100,0xf80e0fa0,0xc0846183,0x7f82008,0x2000000,0xf020100,0x40321360, 0x80200014,0xa3e0201f,0x8207f820,0x8000000,0x0,0x0,0x3e01037,0x207f820,0x201e1,0xfc000200,0x80040000,0x0,0x0,0x1fc000,0x17b0, 0x48000000,0x12,0xc120f0,0x40902409,0x2409028,0x783c7f9f,0xe7f9fe3e,0xf83e0f8,0x7c1061e0,0x781e0781,0xe000be07,0x81e0781e, 0x204017c,0x3e8fa3e8,0xfa3e8fa3,0x70781f07,0xc1f07c7f,0x1fc7f1fc,0x1e1021e0,0x781e0781,0xe0007e0f,0xa3e8fa3e,0x8305e030,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0xc06,0xc,0x100,0x0,0x0,0x0,0x3000,0x0,0x20000000,0x0,0x0,0x0,0x0,0xc000, 0x0,0x0,0x2001,0x1000000,0x0,0x0,0x20000,0x400000,0x0,0x40002000,0x0,0x1,0x2008,0x2000000,0x100,0x40240000,0x80200008,0x40000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80040000,0x0,0x0,0x0,0x1000,0x48000000,0x1f,0x181f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1040010,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000,0x60c,0x18,0x0, 0x0,0x0,0x0,0x6000,0x0,0x10000000,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x3800,0x7000000,0x0,0x0,0x840000,0x400000,0x0,0x40002000, 0x0,0x2,0x2008,0x2000000,0x200,0x40440000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40,0x0,0x80780000,0x0,0x0,0x0,0x1000,0x48000400, 0x2,0x1e02000,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x0,0x0,0x0,0x0,0x0,0x0,0x2040020,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x4000,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3800000,0x0,0x40002000,0x0,0xe,0x1808,0xc000000,0x3,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000000, 0x0,0x0,0x0,0x1000,0x1c00,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x380000,0x0,0x0,0x0,0x0,0x0,0x0,0xe0400e0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 12x24 font const unsigned int font12x24[12*24*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0, 0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c, 0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003, 0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019, 0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000, 0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000, 0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6, 0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f, 0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603, 0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8, 0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3, 0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00, 0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019, 0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc, 0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003, 0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f, 0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006, 0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009, 0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018, 0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00, 0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000, 0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000, 0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3, 0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980, 0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000, 0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60, 0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600, 0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f, 0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600, 0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0, 0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000, 0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606, 0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6, 0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f, 0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001, 0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061, 0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6, 0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000, 0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660, 0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d, 0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180, 0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020, 0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660, 0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060, 0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000, 0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006, 0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06, 0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090, 0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006, 0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066, 0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183, 0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044, 0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001, 0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003, 0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1, 0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f, 0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660, 0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60, 0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000, 0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001, 0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003, 0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603, 0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00, 0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066, 0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, 0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004, 0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006, 0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06, 0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de, 0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6, 0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066, 0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6, 0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000, 0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006, 0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06, 0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc, 0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000, 0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8, 0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000, 0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000, 0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e, 0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c, 0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000, 0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140, 0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060, 0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0, 0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030, 0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60, 0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c, 0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000, 0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240, 0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0, 0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71, 0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300, 0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8, 0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8, 0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0, 0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000, 0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000, 0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83, 0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000, 0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6, 0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6, 0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0, 0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0, 0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f, 0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c, 0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0, 0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0, 0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6, 0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000, 0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000, 0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000, 0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0, 0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000, 0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0, 0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0, 0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 16x32 font const unsigned int font16x32[16*32*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730, 0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180, 0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, 0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380, 0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380, 0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000, 0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070, 0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c, 0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000, 0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700, 0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180, 0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0, 0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000, 0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000, 0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f, 0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0, 0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038, 0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0, 0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0, 0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0, 0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007, 0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0, 0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0, 0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000, 0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc, 0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0, 0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0, 0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0, 0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000, 0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0, 0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e, 0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0, 0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038, 0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0, 0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300, 0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08, 0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380, 0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0, 0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c, 0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00, 0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838, 0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0, 0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0, 0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000, 0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0, 0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0, 0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0, 0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000, 0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000, 0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180, 0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000, 0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000, 0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007, 0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70, 0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180, 0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0, 0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838, 0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180, 0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770, 0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770, 0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc, 0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380, 0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12, 0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770, 0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038, 0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0, 0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380, 0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00, 0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c, 0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400, 0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe, 0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770, 0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038, 0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78, 0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0, 0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c, 0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0, 0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8, 0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0, 0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0, 0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c, 0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c, 0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0, 0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0, 0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800, 0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380, 0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0, 0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860, 0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0, 0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c, 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70, 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe, 0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc, 0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc, 0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70, 0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180, 0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0, 0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0, 0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc, 0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70, 0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe, 0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000, 0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0, 0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc, 0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0, 0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000, 0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc, 0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838, 0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0, 0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000, 0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0, 0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc, 0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0, 0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838, 0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c, 0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0, 0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180, 0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000, 0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0, 0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c, 0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0, 0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838, 0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c, 0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0, 0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180, 0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c, 0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c, 0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0, 0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0, 0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0, 0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8, 0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800, 0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000, 0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000, 0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0, 0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78, 0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000, 0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838, 0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0, 0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c, 0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0, 0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180, 0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18, 0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380, 0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78, 0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0, 0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000, 0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000, 0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c, 0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78, 0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000, 0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8, 0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380, 0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8, 0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380, 0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe, 0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc, 0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc, 0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8, 0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180, 0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8, 0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0, 0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38, 0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000, 0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000, 0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078, 0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0, 0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0, 0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000, 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0, 0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000, 0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000, 0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0, 0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, 0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000, 0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0, 0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000, 0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000, 0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000, 0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 19x38 font const unsigned int font19x38[19*38*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c380000,0x0,0x1c380,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800007,0x3c003,0x86000000, 0x1e00000,0x3,0x80000700,0x3c00000,0x380000,0x70003c00,0x0,0xe1800e,0x1c00,0xf000e18,0x0,0x0,0x700000e0,0x780000,0x7000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe700000,0x0,0xe700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0000e,0x7e003,0xe60071c0,0x7f80000,0x1,0xc0000e00,0x7e0038e,0x1c0000, 0xe0007e00,0x38e00000,0xf98007,0x3800,0x1f800f98,0x1c70000,0x0,0x380001c0,0xfc0071,0xc000e000,0x0,0x0,0x0,0x0,0x3e00000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x7e00000,0x0,0x7e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0001c,0xe7006,0x7c0071c0,0xe180000,0x0,0xe0001c00,0xe70038e,0xe0001,0xc000e700,0x38e00000, 0x19f0003,0x80007000,0x39c019f0,0x1c70000,0x0,0x1c000380,0x1ce0071,0xc001c000,0x0,0x0,0x0,0x0,0x7f00000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000, 0x0,0x3c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x700038,0x1c3806,0x3c0071c0,0xc0c0000,0x0,0x70003800,0x1c38038e,0x70003,0x8001c380,0x38e00000,0x18f0001,0xc000e000, 0x70e018f0,0x1c70000,0x0,0xe000700,0x3870071,0xc0038000,0x0,0x0,0x0,0x0,0xe380000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60000000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c38,0x0,0x1,0xc3800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000003,0x80018000,0x0,0xc180000, 0xe,0x380,0x1800000,0xe00000,0x38001800,0x0,0x38,0xe00,0x6000000,0x0,0x1,0xc0000070,0x300000,0x3800,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78c00,0xc30, 0x0,0x0,0xc3000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800000,0x0,0x0,0x0,0xe0,0x1c000f,0xc0000000,0x0,0x0, 0x0,0xc0c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7000007,0x3c003,0xc6000000,0xc180000,0x7,0x700, 0x3c00000,0x700000,0x70003c00,0x0,0xf1801c,0x1c00,0xf000f18,0x0,0x0,0xe00000e0,0x780000,0x7000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x3800000,0x700000,0x38, 0x7,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf800e,0x3e0000,0x0,0x0,0x0,0x1e00000,0x0,0x1, 0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7cc00,0x660,0x0,0x0,0x66000000,0x0,0x0,0x0,0x0,0x7,0x1c000000,0x0,0x0,0x0,0x3fe00000, 0x0,0x0,0x7000000,0x0,0x0,0x0,0x3e0,0x7c001f,0xe0000000,0x0,0x0,0x0,0xe1c0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x1f80,0x380000e,0x7e007,0xe60071c0,0xc180000,0x3,0x80000e00,0x7e0038e,0x380000,0xe0007e00,0x38e00f00,0x1f9800e, 0x3800,0x1f801f98,0x1c70000,0x0,0x700001c0,0xfc0071,0xc000e007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61c00600,0x1e00007e,0x70000,0x18003000,0x1800000,0x0,0x0,0x1c01f0,0x7e003f,0xc003f800, 0x1e03ffc,0x7f01ff,0xfc03f000,0x7e000000,0x0,0x0,0xfc0,0x1e,0x7fe000,0x7e03fe00,0x3fff07ff,0xe007e038,0x383ffe0,0xff81c01, 0xe1c000f8,0xf8f00e0,0xfc01ffc,0x3f00ff,0xc000fe07,0xfffc7007,0x1c007700,0x73c01ef,0x78ffff,0xfe0380,0xfe000,0x38000000,0x1800000, 0x700000,0x38,0x1f,0xe000001c,0x1c00,0x1c00700,0x7fc0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0xfc0000, 0x0,0x7f00000,0x0,0x1,0x98000000,0x7f00000,0x3ffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0xcf81f,0xee3807e0,0x0,0x0,0x7e03c01e,0x1c, 0x0,0x1f800000,0xf0078038,0xfc007,0x1c000000,0xfe00000,0x0,0x0,0x3fe000f0,0xf,0xc001f800,0x6000000,0xffc000,0x0,0x1c0007e0, 0x360,0x6c0010,0x70000700,0xf0001e,0x3c000,0x78000f00,0x7f800ff,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0, 0x7807007,0xe000fc00,0x1f8003f0,0x7e0000,0x1f867,0x70e00e,0x1c01c380,0x38f00787,0x3fe0,0x180000c,0x66006,0x7c0071c0,0xe380000, 0x1,0x80000c00,0x660038e,0x180000,0xc0006600,0x38e0078e,0x19f0006,0x3000,0x198019f0,0x1c70000,0x0,0x30000180,0xcc0071,0xc000c007, 0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0x61800600,0x7f8001ff,0x70000, 0x38003800,0x1800000,0x0,0x0,0x3807fc,0x1fe00ff,0xf00ffe00,0x3e03ffc,0xff81ff,0xfc07fc01,0xff800000,0x0,0x0,0x3fe0,0xfe001e, 0x7ff801,0xff83ff80,0x3fff07ff,0xe01ff838,0x383ffe0,0xff81c03,0xc1c000f8,0xf8f80e0,0x3ff01fff,0xffc0ff,0xf003ff87,0xfffc7007, 0x1e00f700,0x71c03c7,0x70ffff,0xfe01c0,0xfe000,0x7c000000,0xc00000,0x700000,0x38,0x3f,0xe000001c,0x1c00,0x1c00700,0x7fc0000, 0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x3f800e,0x3f8000,0x0,0x3fe0000,0x0,0xff00000,0x0,0x3,0xc000000,0x1ffc0000,0xfffe00, 0xffff0,0x0,0x0,0x0,0x0,0x0,0xc781f,0xee3803c0,0x0,0x0,0x3c01c01c,0x1c,0xc000,0x7fc00000,0x70070038,0x3fe007,0x1c000000,0x1ff80000, 0x0,0x0,0x3fe003fc,0x1f,0xe003fc00,0xc000000,0x3ffc000,0x0,0x7c000ff0,0x60,0xc0000,0x30000700,0xf0001e,0x3c000,0x78000f00, 0x3f000ff,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x7c0701f,0xf803ff00,0x7fe00ffc,0x1ff8000,0x7fe67, 0x70e00e,0x1c01c380,0x38700707,0x7ff0,0xc00018,0xc3006,0x3c0071c0,0x7f00000,0x0,0xc0001800,0xc30038e,0xc0001,0x8000c300,0x38e003fc, 0x18f0003,0x6000,0x30c018f0,0x1c70000,0x0,0x18000300,0x1860071,0xc0018007,0x38e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe1801fc0,0x618001ff,0x70000,0x30001800,0x21840000,0x0,0x0,0x380ffe,0x1fe00ff, 0xfc0fff00,0x3e03ffc,0x1ff81ff,0xfc0ffe03,0xffc00000,0x0,0x0,0x7ff0,0x3ff803f,0x7ffc03,0xffc3ffc0,0x3fff07ff,0xe03ffc38,0x383ffe0, 0xff81c07,0x81c000f8,0xf8f80e0,0x7ff81fff,0x81ffe0ff,0xf80fff87,0xfffc7007,0xe00e700,0x70e0387,0x80f0ffff,0xe001c0,0xe000, 0xfe000000,0xe00000,0x700000,0x38,0x3c,0x1c,0x1c00,0x1c00700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x78000e,0x3c000, 0x0,0x7ff0000,0x0,0xf100000,0x0,0x7,0xe000000,0x7ffc0000,0x1fffe00,0xffff0,0x0,0x0,0x0,0x0,0x0,0x3,0xf780180,0x0,0x0,0x1801e03c, 0x1c,0xc000,0xffc00000,0x780f0038,0x786000,0x7f00,0x18380000,0x0,0xfe00,0x30c,0x10,0x70020e00,0x1c000000,0x7f8c000,0x0,0x6c001c38, 0x60,0xc0000,0x70000700,0x1f8003f,0x7e000,0xfc001f80,0x3f000ff,0xf03ffc1f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc, 0x7c0703f,0xfc07ff80,0xfff01ffe,0x3ffc000,0xffec7,0x70e00e,0x1c01c380,0x38780f07,0xf070,0xe00038,0x1c3800,0x0,0x3e00000,0x0, 0xe0003800,0x1c380000,0xe0003,0x8001c380,0x3e0,0x3,0x8000e000,0x70e00000,0x0,0x0,0x1c000700,0x3870000,0x38007,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xe3807ff0,0xc0c003c1,0x70000,0x70001c00, 0x718e0000,0x0,0x0,0x700f1e,0x1ce00c0,0x3c0c0f80,0x7e03800,0x3e08000,0x381e0f03,0xc1e00000,0x0,0x0,0x7078,0x783c03f,0x701e07, 0xc1c383e0,0x38000700,0x7c1c38,0x3801c00,0x381c0f,0x1c000fc,0x1f8f80e0,0x78781c07,0x81e1e0e0,0x780f0180,0xe007007,0xe00e380, 0xe0f0783,0x80e0000e,0xe000e0,0xe001,0xef000000,0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000, 0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xf830000,0x0,0x1e000000,0x0,0x0,0x10000,0x780c0000,0x3e38000,0xe0,0x0,0x0,0x0,0x0,0x0,0x3, 0xd580000,0x0,0x0,0xe038,0x1c,0xc000,0xf0400000,0x380e0038,0x702000,0x1ffc0,0xc0000,0x0,0x3ff80,0x606,0x0,0x30000600,0x0, 0x7f8c000,0x0,0xc001818,0x60,0xc0003,0xe0000700,0x1f8003f,0x7e000,0xfc001f80,0x73801ee,0x7c1c1c,0x38000,0x70000e00,0xe0001, 0xc0003800,0x700383e,0x7c0703c,0x3c078780,0xf0f01e1e,0x3c3c000,0xf0f87,0x70e00e,0x1c01c380,0x38380e07,0xe038,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xff0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x1c,0x1c7000,0xc380fff0,0xc0c00380,0x70000,0x70001c00,0x3dbc0070,0x0,0x0,0x701e0f,0xe0000,0x1e000380, 0x6e03800,0x7800000,0x781c0707,0x80e00000,0x0,0x0,0x4038,0xe00c03f,0x700e07,0x4380f0,0x38000700,0x700438,0x3801c00,0x381c0e, 0x1c000ec,0x1b8fc0e0,0xf03c1c03,0xc3c0f0e0,0x3c1e0000,0xe007007,0xe00e380,0xe070703,0xc1e0001e,0xe000e0,0xe001,0xc7000000, 0x0,0x700000,0x38,0x38,0x1c,0x0,0x700,0x1c0000,0x0,0x0,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x70000e,0x1c000,0x0,0xe010000,0x0, 0x1c000000,0x10,0x20000,0x6c000,0xf0000000,0x3838000,0x1e0,0x0,0xf000f,0xf1e00,0x78f00000,0x0,0x3,0xdd80000,0x0,0x0,0xf078, 0x0,0xc001,0xe0000000,0x1c1c0038,0x700000,0x3c1e0,0xc0000,0x0,0x783c0,0x606,0x0,0x30000e00,0x0,0xff8c000,0x0,0xc00300c,0x60, 0xc0003,0xe0000000,0x1f8003f,0x7e000,0xfc001f80,0x73801ce,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x7e07078, 0x1e0f03c1,0xe0783c0f,0x781e000,0x1c0787,0x70e00e,0x1c01c380,0x383c1e07,0xff00e038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x878, 0x0,0x0,0x0,0x7,0x80000080,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c, 0x1c7000,0xc301e630,0xc0c00380,0x70000,0xe0000e00,0xff00070,0x0,0x0,0xe01c07,0xe0000,0xe000380,0xce03800,0x7000000,0x701c0707, 0x600000,0x0,0x4000010,0x38,0x1c00e07f,0x80700e0e,0x38070,0x38000700,0xe00038,0x3801c00,0x381c1c,0x1c000ec,0x1b8ec0e0,0xe01c1c01, 0xc38070e0,0x1c1c0000,0xe007007,0x701c380,0xe078e01,0xc1c0003c,0xe00070,0xe003,0x83800000,0x7f,0x71f000,0x3e003e38,0x3f007ff, 0xe01f1c1c,0x7801fc00,0x3fc00701,0xe01c0077,0x8f071e00,0xf801c7c,0x7c700e,0x3e01fc03,0xfff8380e,0xe007700,0x73c0787,0x387ffc, 0x70000e,0x1c000,0x0,0xe000000,0x0,0x1c000000,0x10,0x20000,0xc2000,0xe0000000,0x3838000,0x3c0,0x0,0xf000f,0x78e00,0x70e00000, 0x0,0x3,0xc980fe0,0x1f0,0xf8000007,0xffc07070,0x0,0x3f801,0xc0000000,0x1e3c0038,0x700000,0x70070,0x7fc0000,0x0,0xe00e0,0x606, 0x1c0000,0x70007c00,0x380e,0xff8c000,0x0,0xc00300c,0x60,0xc0000,0x70000000,0x3fc007f,0x800ff001,0xfe003fc0,0x73801ce,0xe0001c, 0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807,0x7607070,0xe0e01c1,0xc0383807,0x700e000,0x1c0387,0x70e00e,0x1c01c380,0x381c1c07, 0xffc0e0f8,0x3f8007f,0xfe001,0xfc003f80,0x7f007e3,0xe003e001,0xf8003f00,0x7e000fc,0xfe001f,0xc003f800,0x7f00003c,0x38f0007, 0xc000f800,0x1f0003e0,0x7c0007,0x8003f0c3,0x80e0701c,0xe0381c0,0x70700387,0x1f01c00e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0xc0c00380,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03c07, 0x800e0000,0xe000380,0x1ce03800,0x7000000,0x701c0707,0x7003c0,0x780000,0x3c00001e,0x38,0x18006073,0x80700e0e,0x38070,0x38000700, 0xe00038,0x3801c00,0x381c38,0x1c000ee,0x3b8ee0e1,0xe01e1c01,0xc78078e0,0x1c1c0000,0xe007007,0x701c387,0xe03de00,0xe3800038, 0xe00070,0xe007,0x1c00000,0x1ff,0xc077f801,0xff807fb8,0xff807ff,0xe03fdc1d,0xfc01fc00,0x3fc00703,0xc01c007f,0xdf877f00,0x3fe01dfe, 0xff700e,0xff07ff03,0xfff8380e,0x700f700,0x71e0f03,0x80707ffc,0x70000e,0x1c000,0x0,0x1c000008,0x0,0x1c000000,0x10,0x20000, 0x82000,0xe0000000,0x7038000,0x80000380,0x2000040,0x7000e,0x38700,0xf1e00000,0x0,0x3,0xc183ff8,0x3fd,0xfc008007,0xffc038e0, 0x0,0xffc01,0xc0008008,0xe380038,0x380000,0xe3e38,0x1ffc0040,0x80000000,0x1cfc70,0x606,0x1c0000,0xe0007c00,0x380e,0xff8c000, 0x0,0xc00300c,0x8100060,0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0x73801ce,0xe0001c,0x38000,0x70000e00,0xe0001, 0xc0003800,0x7003807,0x77070f0,0xf1e01e3,0xc03c7807,0x8f00f080,0x83c0787,0x70e00e,0x1c01c380,0x380e3807,0xffe0e1c0,0xffe01ff, 0xc03ff807,0xff00ffe0,0x1ffc0ff7,0xf01ff807,0xfc00ff80,0x1ff003fe,0xfe001f,0xc003f800,0x7f0003fc,0x3bf801f,0xf003fe00,0x7fc00ff8, 0x1ff0007,0x8007fd83,0x80e0701c,0xe0381c0,0x70380707,0x7f80e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1c,0x1c701f,0xfff1c600,0x618081c0,0x70000,0xe0000e00,0x3c00070,0x0,0x0,0xe03803,0x800e0000,0xe000380,0x18e03800, 0xf000000,0xf01c0707,0x7003c0,0x780000,0xfc00001f,0x80000078,0x301e6073,0x80700e1c,0x38038,0x38000700,0x1c00038,0x3801c00, 0x381c70,0x1c000e6,0x338ee0e1,0xc00e1c01,0xc70038e0,0x1c1c0000,0xe007007,0x701c387,0xe01dc00,0xf7800078,0xe00070,0xe00e,0xe00000, 0x3ff,0xe07ffc03,0xffc0fff8,0x1ffc07ff,0xe07ffc1d,0xfe01fc00,0x3fc00707,0x801c007f,0xdf877f80,0x7ff01fff,0x1fff00e,0xff07ff03, 0xfff8380e,0x700e380,0xe0e0e03,0x80707ffc,0x70000e,0x1c000,0x0,0x7ffc001c,0x0,0x1c000000,0x10,0x20000,0x82000,0xe0000000, 0x7038001,0xc0000780,0x70000e0,0x3800e,0x38700,0xe1c00000,0x0,0x3,0xc183ff8,0x7ff,0xfc01c007,0xffc03de0,0x0,0x1ffc01,0xc001c01c, 0xf780038,0x3c0000,0xcff18,0x380c00c1,0x80000000,0x18fe30,0x30c,0x1c0001,0xc0000e00,0x380e,0xff8c000,0x0,0xc00300c,0xc180060, 0xc0000,0x30000700,0x39c0073,0x800e7001,0xce0039c0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x877070e0, 0x71c00e3,0x801c7003,0x8e0071c0,0x1c380fc7,0x70e00e,0x1c01c380,0x380f7807,0x1e0e380,0x1fff03ff,0xe07ffc0f,0xff81fff0,0x3ffe0fff, 0xf03ffc0f,0xfe01ffc0,0x3ff807ff,0xfe001f,0xc003f800,0x7f0007fe,0x3bfc03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x800fff83,0x80e0701c, 0xe0381c0,0x70380707,0xffc0e01c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1c701f, 0xfff1c600,0x7f8381e0,0x70000,0xc0000600,0xff00070,0x0,0x0,0x1c03803,0x800e0000,0xe000f00,0x38e03fe0,0xe000000,0xe00e0e07, 0x7003c0,0x780007,0xf0ffff87,0xf00000f0,0x307fe0f3,0xc0703c1c,0x38038,0x38000700,0x1c00038,0x3801c00,0x381ce0,0x1c000e6,0x338e70e1, 0xc00e1c01,0xc70038e0,0x3c1e0000,0xe007007,0x783c38f,0x8e01fc00,0x770000f0,0xe00038,0xe01c,0x700000,0x381,0xe07c1e07,0xc0c1e0f8, 0x3c1e0038,0xf07c1f,0xe001c00,0x1c0070f,0x1c0079,0xf3c7c380,0xf0781f07,0x83c1f00f,0xc10f0300,0x1c00380e,0x700e380,0xe0f1e03, 0xc0f00078,0x70000e,0x1c000,0x0,0xfff8003e,0x0,0x3c000000,0x10,0x20000,0xc6000,0xf0000000,0x7038003,0xe0000f00,0xf8001f0, 0x3801c,0x18300,0xe1800000,0x0,0x3,0xc187818,0x70f,0x9e03e000,0x7801dc0,0x1c,0x3cc401,0xc000efb8,0x7f7f0038,0x3f0000,0x1ce11c, 0x300c01c3,0x80000000,0x38c638,0x3fc,0x1c0003,0x80000600,0x380e,0xff8c000,0x0,0xc00300c,0xe1c0060,0xc0010,0x70000700,0x79e00f3, 0xc01e7803,0xcf0079e0,0xe1c038e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003, 0x8e0070e0,0x38381dc7,0x70e00e,0x1c01c380,0x38077007,0xf0e700,0x1c0f0381,0xe0703c0e,0x781c0f0,0x381e083e,0x787c0c1e,0xf03c1e0, 0x783c0f07,0x800e0001,0xc0003800,0x7000fff,0x3e1c078,0x3c0f0781,0xe0f03c1e,0x783c000,0x1e0f03,0x80e0701c,0xe0381c0,0x70380f07, 0xc1e0e03c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x1,0x8701c600,0x1e0f01e0,0x1, 0xc0000700,0x3dbc0070,0x0,0x0,0x1c03803,0x800e0000,0x1e01fe00,0x70e03ff8,0xe3e0001,0xe007fc07,0x80f003c0,0x78001f,0xc0ffff81, 0xfc0001e0,0x30e1e0e1,0xc07ff81c,0x38038,0x3ffe07ff,0xc1c0003f,0xff801c00,0x381de0,0x1c000e7,0x738e70e1,0xc00e1c03,0xc70038e0, 0x780f8000,0xe007007,0x383838d,0x8e00f800,0x7f0000e0,0xe00038,0xe000,0x0,0x200,0xf0780e07,0x8041c078,0x380e0038,0xe03c1e, 0xf001c00,0x1c0071e,0x1c0070,0xe1c783c0,0xe0381e03,0x8380f00f,0xe0000,0x1c00380e,0x381c380,0xe07bc01,0xc0e00078,0x70000e, 0x1c000,0x0,0x1c000061,0x0,0x38000000,0x10,0x20000,0x7c000,0x7c000000,0x703fc06,0x10000e00,0x18400308,0x1801c,0x1c381,0xc3800000, 0x0,0x0,0x7000,0xe0f,0xe061000,0x7801fc0,0x1c,0x38c001,0xc0007ff0,0x7fff0038,0x77c000,0x19c00c,0x301c0387,0x0,0x30c618,0xf0, 0x1c0007,0x600,0x380e,0x7f8c007,0x80000000,0xc001818,0x70e03fc,0x387f871f,0xe0e00700,0x70e00e1,0xc01c3803,0x870070e0,0xe1c038f, 0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001,0xc0003800,0x7003803,0x873870e0,0x71c00e3,0x801c7003,0x8e007070,0x703839c7,0x70e00e, 0x1c01c380,0x3807f007,0x70e700,0x10078200,0xf0401e08,0x3c10078,0x200f001c,0x3878041c,0x70380e0,0x701c0e03,0x800e0001,0xc0003800, 0x7001e0f,0x3c1e070,0x1c0e0381,0xc070380e,0x701c000,0x1c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80e07038,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600e600,0x7803f0,0x1,0xc0000700,0x718e0070,0x0,0x0,0x38038c3, 0x800e0000,0x3c01f800,0x60e03ffc,0xeff8001,0xc001f003,0xc1f003c0,0x7800fe,0xffff80,0x3f8003c0,0x60c0e0e1,0xc07fe01c,0x38038, 0x3ffe07ff,0xc1c07e3f,0xff801c00,0x381fe0,0x1c000e3,0x638e30e1,0xc00e1c07,0x870038ff,0xf00ff800,0xe007007,0x38381cd,0x9c007000, 0x3e0001e0,0xe0001c,0xe000,0x0,0x0,0x70780f0f,0x3c078,0x70070038,0x1e03c1c,0x7001c00,0x1c0073c,0x1c0070,0xe1c701c1,0xe03c1e03, 0xc780f00f,0xe0000,0x1c00380e,0x381c387,0xe03f801,0xc0e000f0,0x70000e,0x1c007,0xe0100000,0x1c0000cd,0x80000003,0xffc00000, 0x3ff,0x807ff000,0xe0,0x7fc00060,0x703fc0c,0xd8001e00,0x3360066c,0x1c018,0xc181,0x83000000,0x0,0x0,0x7000,0x300e07,0xe0cd800, 0xf000f80,0x1c,0x78c00f,0xff0038e0,0x3e00038,0xe1e000,0x19800c,0x383c070e,0x7fffc00,0x30fc18,0x0,0xffff80e,0x20e00,0x380e, 0x7f8c007,0x80000000,0xc001c38,0x38703ff,0xf87fff0f,0xcfe00f00,0x70e00e1,0xc01c3803,0x870070e0,0x1e1e078f,0xe1c0001f,0xff03ffe0, 0x7ffc0fff,0x800e0001,0xc0003800,0x700ff83,0x871870e0,0x71c00e3,0x801c7003,0x8e007038,0xe03871c7,0x70e00e,0x1c01c380,0x3803e007, 0x70e700,0x38000,0x70000e00,0x1c00038,0x7001c,0x38f00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7001c07,0x380e0f0,0x1e1e03c3, 0xc078780f,0xf01e000,0x3c0f03,0x80e0701c,0xe0381c0,0x701c0e07,0x80f07038,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0x8600ff00,0x1e00778,0x38000001,0xc0000700,0x21843fff,0xe0000000,0x0,0x38039e3,0x800e0000, 0x7c01fe00,0xe0e0203e,0xeffc001,0xc00ffe03,0xff700000,0x7f0,0x0,0x7f00380,0x618060e1,0xc07ffc1c,0x38038,0x3ffe07ff,0xc1c07e3f, 0xff801c00,0x381ff0,0x1c000e3,0x638e38e1,0xc00e1fff,0x870038ff,0xc003fe00,0xe007007,0x38381cd,0x9c00f800,0x3e0003c0,0xe0001c, 0xe000,0x0,0x0,0x7070070e,0x38038,0x70070038,0x1c01c1c,0x7001c00,0x1c00778,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0xfc000, 0x1c00380e,0x381c3c7,0x1e01f001,0xe1e001e0,0xf0000e,0x1e01f,0xf8300000,0x1c00019c,0xc0000003,0xffc00000,0x10,0x20000,0x700, 0x1ff000c0,0x703fc19,0xcc003c00,0x67300ce6,0xc038,0xc181,0x83000000,0x0,0x0,0x7e00,0x180e07,0xe19cc00,0x1e000f80,0x1c,0x70c00f, 0xff007070,0x3e00038,0xe0f000,0x19800c,0x1fec0e1c,0x7fffc00,0x30f818,0x0,0xffff81f,0xf003fc00,0x380e,0x3f8c007,0x80000000, 0x7f800ff0,0x1c3803f,0xe007fc00,0xff800e00,0x70e00e1,0xc01c3803,0x870070e0,0x1c0e070f,0xe1c0001f,0xff03ffe0,0x7ffc0fff,0x800e0001, 0xc0003800,0x700ff83,0x871c70e0,0x71c00e3,0x801c7003,0x8e00701d,0xc038e1c7,0x70e00e,0x1c01c380,0x3803e007,0x70e3c0,0x38000, 0x70000e00,0x1c00038,0x7001c,0x38e00038,0x3870070,0xe00e1c01,0xc00e0001,0xc0003800,0x7003c07,0x8380e0e0,0xe1c01c3,0x80387007, 0xe00e1ff,0xfe381b83,0x80e0701c,0xe0381c0,0x701e1e07,0x707878,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1c,0x3,0xe007fe0,0x7800e3c,0x38000001,0xc0000700,0x1803fff,0xe0000000,0x0,0x70039c3,0x800e0000,0xf8000f80, 0xc0e0000e,0xf83c003,0xc01e0f01,0xff700000,0x7c0,0x0,0x1f00780,0x618061c0,0xe0701e1c,0x38038,0x38000700,0x1c07e38,0x3801c00, 0x381e78,0x1c000e3,0xe38e18e1,0xc00e1fff,0x70038ff,0xe0007f80,0xe007007,0x1c701dd,0x9c00f800,0x1c000780,0xe0000e,0xe000,0x0, 0x7f,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x7fc00,0x1c00380e, 0x1c381c7,0x1c01f000,0xe1c001c0,0xfe0000e,0xfe1f,0xfff00000,0x7ff003fc,0xe0000003,0xffc00000,0x10,0x20000,0x3800,0x3fc0180, 0x703803f,0xce007800,0xff381fe7,0x30,0x0,0xc0,0x0,0x0,0x3fe0,0xc0e07,0xfe3fce00,0x1c000700,0x1c,0x70c00f,0xff006030,0x1c00000, 0xe07800,0x19800c,0xfcc1c38,0x7fffc00,0x30d818,0x0,0xffff81f,0xf001f800,0x380e,0xf8c007,0x80000000,0x7f8007e0,0xe1c3fe,0x7fc00f, 0xf8001e00,0xe0701c0,0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700ff83,0x870c70e0, 0x71c00e3,0x801c7003,0x8e00700f,0x8038c1c7,0x70e00e,0x1c01c380,0x3801c007,0xf0e3e0,0x3ff807f,0xf00ffe01,0xffc03ff8,0x7ff03ff, 0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe383383,0x80e0701c, 0xe0381c0,0x700e1c07,0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x3,0xc000ff0, 0x3c1e1c1c,0x38000001,0xc0000700,0x1803fff,0xe0000007,0xf8000000,0x7003803,0x800e0001,0xf0000381,0xc0e00007,0xf01e003,0x801c0700, 0x7c700000,0x7c0,0x0,0x1f00700,0x618061c0,0xe0700e1c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381e38,0x1c000e1,0xc38e1ce1, 0xc00e1ffc,0x70038e0,0xf0000780,0xe007007,0x1c701dd,0xdc01fc00,0x1c000780,0xe0000e,0xe000,0x0,0x1ff,0xf070070e,0x38038,0x7fff0038, 0x1c01c1c,0x7001c00,0x1c007f8,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3ff00,0x1c00380e,0x1c381cd,0x9c00e000,0xe1c003c0, 0xf80000e,0x3e18,0x3ff00000,0xffe007fd,0xf0000000,0x38000000,0x10,0x20000,0x1c000,0x3c0300,0x703807f,0xdf007801,0xff7c3fef, 0x80000000,0x0,0x3e0,0x7ffe7ff,0xff000000,0x1ff8,0x60e07,0xfe7fdf00,0x3c000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0xf03800, 0x19800c,0x1c38,0x1c07,0xf830cc18,0x0,0x1c0000,0x0,0x380e,0x18c007,0x80000000,0x0,0xe1cfe0,0x1fc003f,0x80003c00,0xe0701c0, 0xe0381c07,0x380e070,0x1c0e070e,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003, 0x8e007007,0x3981c7,0x70e00e,0x1c01c380,0x3801c007,0x1e0e0f8,0xfff81ff,0xf03ffe07,0xffc0fff8,0x1fff07ff,0xf8e0003f,0xff87fff0, 0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e1ff,0xfe386383,0x80e0701c,0xe0381c0,0x700e1c07, 0x703870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0x7f,0xffc00678,0x707f9c1e,0x38000001, 0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0003,0xe00001c3,0x80e00007,0xe00e007,0x80380380,0x700000,0x7f0,0x0,0x7f00700, 0x618061ff,0xe070071c,0x38038,0x38000700,0x1c00e38,0x3801c00,0x381c3c,0x1c000e1,0xc38e1ce1,0xc00e1c00,0x70038e0,0x700003c0, 0xe007007,0x1c701d8,0xdc03dc00,0x1c000f00,0xe00007,0xe000,0x0,0x3ff,0xf070070e,0x38038,0x7fff0038,0x1c01c1c,0x7001c00,0x1c007fc, 0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x3f00,0x1c00380e,0x1c381cd,0x9c01f000,0x73800780,0xfe0000e,0xfe10,0x7c00000,0x1c000ffb, 0xf8000000,0x38000000,0x10,0x20000,0x20000,0x1e0700,0x70380ff,0xbf80f003,0xfefe7fdf,0xc0000000,0x0,0x3f0,0x7ffe7ff,0xff000000, 0x1f8,0x30e07,0xfeffbf80,0x78000700,0x1c,0x70c001,0xc0006030,0x7fff0000,0x783800,0x1ce11c,0xe1c,0x1c07,0xf838ce38,0x0,0x1c0000, 0x0,0x380e,0x18c000,0x0,0x0,0x1c38c00,0x1800030,0x7800,0xfff01ff,0xe03ffc07,0xff80fff0,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00, 0xe0001,0xc0003800,0x7003803,0x870e70e0,0x71c00e3,0x801c7003,0x8e00700f,0x803b81c7,0x70e00e,0x1c01c380,0x3801c007,0xffe0e03c, 0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0fff,0xf8e0003f,0xff87fff0,0xfffe1fff,0xc00e0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3, 0x80387007,0xe00e000,0x38c383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0063c,0x40619c0f,0x30000001,0xc0000700,0x70,0x7,0xf8000000,0xe003803,0x800e0007,0xc00001c3, 0xfffc0007,0xe00e007,0x380380,0xf00000,0xfe,0xffff80,0x3f800700,0x618063ff,0xf070071c,0x38038,0x38000700,0x1c00e38,0x3801c00, 0x381c1e,0x1c000e0,0x38e0ee1,0xc00e1c00,0x70038e0,0x380001c0,0xe007007,0x1ef01d8,0xdc038e00,0x1c001e00,0xe00007,0xe000,0x0, 0x7c0,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0079e,0x1c0070,0xe1c701c1,0xc01c1c01,0xc700700e,0x780,0x1c00380e, 0xe701cd,0x9c01f000,0x73800f00,0xe0000e,0xe000,0x0,0x1c0007f7,0xf0000000,0x70000000,0x10,0x20000,0x0,0xe0e00,0x703807f,0x7f01e001, 0xfdfc3fbf,0x80000000,0x0,0x7f0,0x0,0x0,0x3c,0x18e07,0x7f7f00,0xf0000700,0x1c,0x70c001,0xc0007070,0x1c00000,0x3e7000,0xcff18, 0x3ffc070e,0x1c07,0xf818c630,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x3870000,0xe000fc00,0x380f000,0x1fff83ff,0xf07ffe0f, 0xffc1fff8,0x3fff0ffe,0x1c0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003803,0x870770e0,0x71c00e3,0x801c7003,0x8e00701d, 0xc03f01c7,0x70e00e,0x1c01c380,0x3801c007,0xffc0e01c,0x3e0387c0,0x70f80e1f,0x1c3e038,0x7c071e1c,0xe00038,0x70000,0xe0001c00, 0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x398383,0x80e0701c,0xe0381c0,0x70073807,0x701ce0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f,0xffc0061c,0xc0dc07,0xf0000001,0xc0000700, 0x70,0x0,0x0,0x1c003c07,0x800e000f,0x1c3,0xfffc0007,0xe00e007,0x380380,0xe00000,0x1f,0xc0ffff81,0xfc000700,0x618063ff,0xf070070e, 0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0e,0x1c000e0,0x38e0ee1,0xe01e1c00,0x78078e0,0x380001c0,0xe007007,0xee01f8,0xfc078f00, 0x1c001c00,0xe00003,0x8000e000,0x0,0x700,0x7070070e,0x38038,0x70000038,0x1c01c1c,0x7001c00,0x1c0070e,0x1c0070,0xe1c701c1, 0xc01c1c01,0xc700700e,0x380,0x1c00380e,0xe700ed,0xb803f800,0x77800f00,0x70000e,0x1c000,0x0,0xe0003f7,0xe0000000,0x70000000, 0x10,0x20000,0x1c0e0,0xe1c00,0x703803f,0x7e01c000,0xfdf81fbf,0x0,0x0,0x3f0,0x0,0x0,0x1c,0x1ce07,0x3f7e00,0xf0000700,0x1c, 0x70c001,0xc00038e0,0x1c00038,0xf7000,0xe3e38,0x3ffc0387,0x1c00,0x1cc770,0x0,0x1c0000,0x0,0x380e,0x18c000,0x0,0x3ffc,0x70e0001, 0xe001fe00,0x780e000,0x1fff83ff,0xf07ffe0f,0xffc1fff8,0x3fff0ffe,0xe0001c,0x38000,0x70000e00,0xe0001,0xc0003800,0x7003807, 0x70770f0,0xf1e01e3,0xc03c7807,0x8f00f038,0xe03e03c7,0x70e00e,0x1c01c380,0x3801c007,0xff00e00e,0x38038700,0x70e00e1c,0x1c38038, 0x70071c1c,0xe00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003803,0x8380e0e0,0xe1c01c3,0x80387007,0xe00e000,0x3b0383,0x80e0701c, 0xe0381c0,0x70077807,0x701de0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x1c00061c, 0xc0de03,0xe0000001,0xc0000700,0x70,0x0,0x0,0x1c001c07,0xe001e,0x1c3,0xfffc0007,0x600e00e,0x380380,0xe00000,0x7,0xf0ffff87, 0xf0000000,0x60c0e380,0x7070070e,0x38070,0x38000700,0xe00e38,0x3801c00,0x381c0f,0x1c000e0,0x38e06e0,0xe01c1c00,0x38070e0, 0x1c0001c0,0xe007007,0xee00f8,0xf80f0700,0x1c003c00,0xe00003,0x8000e000,0x0,0x700,0x70780f0f,0x3c078,0x70000038,0x1e03c1c, 0x7001c00,0x1c0070f,0x1c0070,0xe1c701c1,0xe03c1e03,0xc780f00e,0x380,0x1c00380e,0xe700f8,0xf807bc00,0x3f001e00,0x70000e,0x1c000, 0x0,0xe0001ff,0xc0000000,0x70000000,0x10,0x20000,0x33110,0xe0e00,0x383801f,0xfc03c000,0x7ff00ffe,0x0,0x0,0x3e0,0x0,0x0,0x1c, 0x38e07,0x1ffc01,0xe0000700,0x1c,0x78c001,0xc0007ff0,0x1c00038,0x7c000,0x70070,0x1c3,0x80001c00,0xe00e0,0x0,0x1c0000,0x0, 0x380e,0x18c000,0x0,0x0,0xe1c0001,0xe0010700,0x780e000,0x1c038380,0x70700e0e,0x1c1c038,0x78070e0e,0xe0001c,0x38000,0x70000e00, 0xe0001,0xc0003800,0x7003807,0x7037070,0xe0e01c1,0xc0383807,0x700e070,0x701c0387,0x70e00e,0x1c01c380,0x3801c007,0xe00e,0x38038700, 0x70e00e1c,0x1c38038,0x70071c1c,0xf00038,0x70000,0xe0001c00,0xe0001,0xc0003800,0x7003c07,0x8380e0f0,0x1e1e03c3,0xc078780f, 0xf01e007,0x803e0783,0x80e0701c,0xe0381c0,0x7003f007,0x80f00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x6,0x1800061c,0xc0de01,0xc0000000,0xc0000e00,0x70,0xf0000,0x3c00,0x38001c0f,0xe003c,0x3c0,0xe0000e,0x701e00e, 0x3c0780,0x1e003c0,0x780000,0xfc00001f,0x80000000,0x60e1e780,0x78700f07,0x4380f0,0x38000700,0xf00e38,0x3801c00,0xc0781c07, 0x81c000e0,0x38e07e0,0xe03c1c00,0x380f0e0,0x1e0003c0,0xe00780f,0xee00f0,0x780e0780,0x1c007800,0xe00001,0xc000e000,0x0,0x700, 0xf0780e07,0x8041c078,0x38020038,0xe03c1c,0x7001c00,0x1c00707,0x801c0070,0xe1c701c0,0xe0381e03,0x8380f00e,0x80380,0x1c003c1e, 0x7e00f8,0xf80f1e00,0x3f003c00,0x70000e,0x1c000,0x0,0xf0100f7,0x80078000,0x700078f0,0x10,0x7ff000,0x61208,0x1e0700,0x383800f, 0x78078000,0x3de007bc,0x0,0x0,0x0,0x0,0x0,0x401c,0x70e0f,0xf7803,0xc0000700,0x1c,0x38c001,0xc000efb8,0x1c00038,0x1e000,0x3c1e0, 0xc1,0x80000000,0x783c0,0x0,0x0,0x0,0x3c1e,0x18c000,0x0,0x0,0xc180003,0x60000300,0xd80e010,0x3c03c780,0x78f00f1e,0x1e3c03c, 0x70039c0e,0x70041c,0x38000,0x70000e00,0xe0001,0xc0003800,0x700380f,0x703f070,0x1e0e03c1,0xc078380f,0x701e0e0,0x381c0787, 0x80f0f01e,0x1e03c3c0,0x7801c007,0xe00e,0x38078700,0xf0e01e1c,0x3c38078,0x700f1c1c,0x78041c,0x1038020,0x70040e00,0x800e0001, 0xc0003800,0x7001c07,0x380e070,0x1c0e0381,0xc070380e,0x701c007,0x801e0703,0xc1e0783c,0xf0781e0,0xf003f007,0x80e00fc0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xe,0x1801867c,0xc0cf83,0xe0000000,0xe0000e00, 0x70,0xf0000,0x3c00,0x38000f1e,0xe0070,0x180780,0xe0603e,0x783c01e,0x1e0f01,0x7c003c0,0x780000,0x3c00001e,0x700,0x307fe700, 0x38701e07,0xc1c383e0,0x38000700,0x7c1e38,0x3801c00,0xe0f01c03,0x81c000e0,0x38e03e0,0x78781c00,0x1e1e0e0,0xe180780,0xe003c1e, 0x7c00f0,0x781e03c0,0x1c007000,0xe00001,0xc000e000,0x0,0x783,0xf07c1e07,0xc0c1e0f8,0x3e0e0038,0xf07c1c,0x7001c00,0x1c00703, 0xc01e0070,0xe1c701c0,0xf0781f07,0x83c1f00e,0xe0f80,0x1e003c3e,0x7e00f8,0xf80e0e00,0x3f003800,0x70000e,0x1c000,0x0,0x7830077, 0xf0000,0x700078f0,0x10,0x20000,0x41208,0xc03c0380,0x3c38007,0x70070000,0x1dc003b8,0x0,0x0,0x0,0x0,0x0,0x707c,0x6070f,0x86077003, 0x80000700,0x1c,0x3ec401,0xc001c01c,0x1c00038,0xf000,0x1ffc0,0x40,0x80000000,0x3ff80,0x0,0x0,0x0,0x3e3e,0x18c000,0x0,0x0, 0x8100006,0x60000300,0x1980f070,0x3801c700,0x38e0071c,0xe3801c,0x70039c0e,0x7c1c1c,0x38000,0x70000e00,0xe0001,0xc0003800, 0x700383e,0x701f03c,0x3c078780,0xf0f01e1e,0x3c3c1c0,0x1c3f0f03,0xc1e0783c,0xf0781e0,0xf001c007,0xe81e,0x3c1f8783,0xf0f07e1e, 0xfc3c1f8,0x783f1e3e,0x187c0c1f,0x703e0e0,0x7c1c0f83,0x800e0001,0xc0003800,0x7001e0f,0x380e078,0x3c0f0781,0xe0f03c1e,0x783c007, 0x801e0f03,0xc3e0787c,0xf0f81e1,0xf003f007,0xc1e00fc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x1c,0xe,0x3801fff8,0x6187ff,0xe0000000,0xe0000e00,0x70,0xf0000,0x3c00,0x38000ffe,0x1fff0ff,0xfe1fff80,0xe07ffc,0x3ffc01c, 0x1fff01,0xff8003c0,0x780000,0x4000010,0x700,0x301e6700,0x387ffe03,0xffc3ffc0,0x3fff0700,0x3ffe38,0x383ffe0,0xfff01c03,0xc1fff8e0, 0x38e03e0,0x7ff81c00,0x1ffe0e0,0xf1fff80,0xe003ffe,0x7c00f0,0x781c01c0,0x1c00ffff,0xe00001,0xc000e000,0x0,0x3ff,0x707ffc03, 0xffc0fff8,0x1ffe0038,0x7ffc1c,0x707fff0,0x1c00701,0xc00ff070,0xe1c701c0,0x7ff01fff,0x1fff00e,0xfff00,0xff81fee,0x7e00f0, 0x781e0f00,0x1e007ffc,0x70000e,0x1c000,0x0,0x3ff003e,0xf0000,0xe00070e0,0x60830010,0x20000,0x41208,0xfffc01c0,0x1fffe03,0xe00ffff0, 0xf8001f0,0x0,0x0,0x0,0x0,0x0,0x7ff8,0xc07fd,0xfe03e007,0xffc00700,0x1c,0x1ffc1f,0xffc08008,0x1c00038,0x7000,0x7f00,0x0,0x0, 0xfe00,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0x6,0x60000700,0x19807ff0,0x3801c700,0x38e0071c,0xe3801c,0x70039c0f,0xf03ffc1f, 0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ffc,0x701f03f,0xfc07ff80,0xfff01ffe,0x3ffc080,0x83fff03,0xffe07ffc,0xfff81ff, 0xf001c007,0xeffc,0x1ffb83ff,0x707fee0f,0xfdc1ffb8,0x3ff70ff7,0xf83ffc0f,0xff01ffe0,0x3ffc07ff,0x83fff87f,0xff0fffe1,0xfffc0ffe, 0x380e03f,0xf807ff00,0xffe01ffc,0x3ff8007,0x803ffe01,0xfee03fdc,0x7fb80ff,0x7001e007,0xffc00780,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x3801fff0,0x7f83fe,0x70000000,0xe0000e00,0x0,0xf0000,0x3c00,0x700007fc, 0x1fff0ff,0xfe1ffe00,0xe07ff8,0x1ff801c,0xffe01,0xff0003c0,0x780000,0x0,0x700,0x38000f00,0x3c7ffc01,0xff83ff80,0x3fff0700, 0x1ffc38,0x383ffe0,0x7fe01c01,0xe1fff8e0,0x38e03e0,0x3ff01c00,0xffc0e0,0x71fff00,0xe001ffc,0x7c00f0,0x783c01e0,0x1c00ffff, 0xe00000,0xe000e000,0x0,0x1ff,0x7077f801,0xff807fb8,0xffc0038,0x3fdc1c,0x707fff0,0x1c00701,0xe007f070,0xe1c701c0,0x3fe01dfe, 0xff700e,0x7fe00,0xff80fee,0x3c0070,0x703c0780,0x1e007ffc,0x70000e,0x1c000,0x0,0x1fe001c,0xe0000,0xe000e1c0,0x71c78010,0x20000, 0x21318,0xfff800c0,0xfffe01,0xc00ffff0,0x70000e0,0x0,0x0,0x0,0x0,0x0,0x3ff0,0x1803fd,0xfe01c007,0xffc00700,0x1c,0xffc1f,0xffc00000, 0x1c00038,0x7000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x3ff7,0x8018c000,0x0,0x0,0xc,0x60000e00,0x31803fe0,0x7801ef00,0x3de007bc, 0xf7801e,0xf003fc0f,0xf01ff81f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83ff8,0x701f01f,0xf803ff00,0x7fe00ffc,0x1ff8000, 0x67fe01,0xffc03ff8,0x7ff00ff,0xe001c007,0xeff8,0xffb81ff,0x703fee07,0xfdc0ffb8,0x1ff70ff7,0xf81ff807,0xfe00ffc0,0x1ff803ff, 0x3fff87f,0xff0fffe1,0xfffc07fc,0x380e01f,0xf003fe00,0x7fc00ff8,0x1ff0000,0x37fc00,0xfee01fdc,0x3fb807f,0x7001e007,0x7f800780, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c,0xc,0x30007fc0,0x1e00f8,0x78000000,0x70001c00, 0x0,0xe0000,0x3c00,0x700001f0,0x1fff0ff,0xfe07f800,0xe01fe0,0x7e0038,0x3f800,0xfc0003c0,0x700000,0x0,0x700,0x18000e00,0x1c7ff000, 0x7e03fe00,0x3fff0700,0x7f038,0x383ffe0,0x1f801c00,0xf1fff8e0,0x38e01e0,0xfc01c00,0x3f80e0,0x787fc00,0xe0007f0,0x7c00f0,0x387800f0, 0x1c00ffff,0xe00000,0xe000e000,0x0,0xfc,0x7071f000,0x3f003e38,0x3f00038,0x1f1c1c,0x707fff0,0x1c00700,0xf003f070,0xe1c701c0, 0x1f801c7c,0x7c700e,0x1f800,0x3f8078e,0x3c0070,0x707803c0,0x1c007ffc,0x70000e,0x1c000,0x0,0x7c0008,0x1e0000,0xe000e1c0,0x71c30010, 0x20000,0x1e1f0,0x3fe00020,0x3ffe00,0x800ffff0,0x2000040,0x0,0x0,0x0,0x0,0x0,0xfc0,0x3001f0,0x78008007,0xffc00700,0x1c,0x3f81f, 0xffc00000,0x1c00038,0x407000,0x0,0x0,0x0,0x0,0x0,0xffff800,0x0,0x39c7,0x18c000,0x0,0x0,0x18,0x60001c00,0x61801f80,0x7000ee00, 0x1dc003b8,0x77000e,0xe001f80f,0xf007e01f,0xff83fff0,0x7ffe0fff,0xc1fff03f,0xfe07ffc0,0xfff83fc0,0x700f007,0xe000fc00,0x1f8003f0, 0x7e0000,0xe1f800,0x7f000fe0,0x1fc003f,0x8001c007,0xe7f0,0x7e380fc,0x701f8e03,0xf1c07e38,0xfc703c1,0xe003f001,0xf8003f00, 0x7e000fc,0x3fff87f,0xff0fffe1,0xfffc03f8,0x380e00f,0xc001f800,0x3f0007e0,0xfc0000,0x61f800,0x78e00f1c,0x1e3803c,0x7001c007, 0x1f000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x70001c00,0x0, 0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, 0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, 0x70000e,0x1c000,0x0,0x0,0x1c0000,0xe000c180,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x70e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x2000,0x0,0x1f,0xf8003800,0x7fe00000,0x0,0x0,0x0,0x0,0x4000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400000, 0x0,0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x30001800, 0x0,0x1c0000,0x0,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e000, 0x0,0x0,0x0,0x0,0x0,0xe00000,0x7000e000,0x0,0x0,0x0,0x0,0x0,0x1c00,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x1c000000, 0x70000e,0x1c000,0x0,0x0,0x1c0001,0xe001c380,0x10,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x7fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x3000,0x0,0x1f,0xf8007000,0x7fe00000,0x0,0x0,0x0,0x0,0x6000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1c007,0x700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x38003800, 0x0,0x380000,0x1,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00000,0x0,0x0,0x3c18000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf000, 0x0,0x0,0x0,0x0,0x0,0xfe0000,0x380fe000,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x38000000, 0x78000e,0x3c000,0x0,0x0,0x180001,0xc0018300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0, 0x38,0x1f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x18c000,0x1800,0x0,0x0,0x6000e000,0x1800000,0x0,0x0,0x0,0x0,0x3000,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x38007,0xe00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x18003000, 0x0,0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0, 0x0,0x0,0x0,0xfe0000,0xfe000,0x0,0x0,0x0,0x0,0x0,0x607800,0x0,0x3c00000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x0,0x78000000, 0x3f800e,0x3f8000,0x0,0x0,0x300043,0xc0018200,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000, 0x0,0x38,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x11800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x23000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x23000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78007, 0x1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x1c007000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe0000, 0xfe000,0x0,0x0,0x0,0x0,0x0,0x7ff000,0x0,0x7f800000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf8000000,0x3f800e,0x3f8000,0x0, 0x0,0x10007f,0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x38,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x3800,0x0,0x1f800,0x0,0x0,0x6001ff00,0x1800000,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f8007,0xfe00,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x7fe000,0x0, 0x7f000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xf0000000,0xf800e,0x3e0000,0x0,0x0,0x7f,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x1f000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3f0007,0xfc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x1fc000,0x0,0x7e000000,0x0,0x0,0x1c00,0x7000,0x0,0x0,0x0,0x3,0xc0000000,0xe,0x0, 0x0,0x0,0x3e,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x3800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0007,0xf000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 29x57 font const unsigned int font29x57[29*57*256/32] = { 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7, 0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000, 0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000, 0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000, 0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0, 0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f, 0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00, 0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800, 0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3, 0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e, 0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0, 0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003, 0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0, 0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0, 0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0, 0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00, 0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0, 0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000, 0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0, 0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0, 0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0, 0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0, 0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000, 0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000, 0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000, 0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800, 0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3, 0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0, 0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000, 0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80, 0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0, 0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000, 0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000, 0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000, 0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f, 0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000, 0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc, 0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00, 0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e, 0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000, 0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe, 0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000, 0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff, 0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00, 0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80, 0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000, 0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0, 0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff, 0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0, 0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003, 0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d, 0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc, 0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff, 0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff, 0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000, 0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0, 0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f, 0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000, 0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0, 0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff, 0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8, 0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003, 0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380, 0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000, 0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80, 0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff, 0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c, 0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000, 0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80, 0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00, 0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000, 0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe, 0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800, 0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f, 0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700, 0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f, 0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007, 0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007, 0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f, 0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0, 0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000, 0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000, 0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000, 0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000, 0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007, 0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003, 0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000, 0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007, 0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00, 0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1, 0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0, 0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c, 0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000, 0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00, 0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000, 0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007, 0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80, 0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0, 0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f, 0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800, 0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000, 0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000, 0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000, 0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0, 0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00, 0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803, 0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f, 0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780, 0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e, 0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000, 0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0, 0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0, 0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078, 0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007, 0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780, 0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e, 0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003, 0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800, 0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001, 0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc, 0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000, 0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03, 0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0, 0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000, 0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, 0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc, 0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0, 0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000, 0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780, 0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0, 0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000, 0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f, 0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff, 0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000, 0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0, 0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c, 0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0, 0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000, 0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00, 0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f, 0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f, 0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f, 0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e, 0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001, 0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001, 0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f, 0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780, 0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007, 0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000, 0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0, 0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787, 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f, 0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001, 0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f, 0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001, 0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00, 0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0, 0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079, 0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c, 0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00, 0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f, 0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000, 0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000, 0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780, 0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00, 0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000, 0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e, 0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff, 0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff, 0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000, 0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e, 0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c, 0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001, 0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801, 0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc, 0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780, 0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f, 0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000, 0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078, 0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001, 0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00, 0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8, 0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0, 0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007, 0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00, 0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0, 0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007, 0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807, 0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c, 0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000, 0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18, 0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0, 0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe, 0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078, 0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, 0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008, 0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0, 0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003, 0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00, 0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80, 0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e, 0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000, 0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000, 0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c, 0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00, 0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00, 0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000, 0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00, 0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000, 0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00, 0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c, 0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00, 0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0, 0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078, 0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000, 0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0, 0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c, 0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000, 0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0, 0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e, 0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c, 0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0, 0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e, 0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0, 0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007, 0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000, 0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800, 0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1, 0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff, 0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000, 0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000, 0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000, 0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000, 0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003, 0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce, 0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00, 0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f, 0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0, 0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e, 0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800, 0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0, 0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007, 0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00, 0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff, 0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0, 0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0, 0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00, 0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000, 0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000, 0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f, 0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0, 0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007, 0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8, 0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0, 0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000, 0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00, 0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80, 0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0, 0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e, 0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c, 0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f, 0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c, 0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003, 0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000, 0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303, 0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc, 0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000, 0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780, 0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000, 0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078, 0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007, 0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00, 0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f, 0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0, 0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c, 0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000, 0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000, 0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000, 0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800, 0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0, 0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0, 0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003, 0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00, 0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f, 0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3, 0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070, 0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0, 0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0, 0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e, 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0, 0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000, 0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80, 0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303, 0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00, 0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe, 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07, 0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f, 0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f, 0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007, 0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800, 0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007, 0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003, 0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0, 0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00, 0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00, 0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000, 0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000, 0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c, 0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01, 0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000, 0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800, 0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000, 0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070, 0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0, 0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0, 0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e, 0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0, 0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff, 0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001, 0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000, 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801, 0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe, 0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, 0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000, 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000, 0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800, 0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000, 0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, 0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800, 0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0, 0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078, 0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000, 0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0, 0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000, 0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0, 0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000, 0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078, 0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001, 0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e, 0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007, 0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001, 0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007, 0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000, 0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003, 0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff, 0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00, 0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80, 0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000, 0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00, 0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000, 0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00, 0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000, 0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000, 0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800, 0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001, 0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00, 0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003, 0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001, 0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800, 0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000, 0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03, 0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001, 0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000, 0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000, 0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c, 0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00, 0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff, 0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e, 0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c, 0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f, 0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e, 0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f, 0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000, 0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0, 0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807, 0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f, 0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80, 0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000, 0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800, 0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00, 0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03, 0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800, 0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, 0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000, 0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff, 0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c, 0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81, 0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000, 0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0, 0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c, 0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00, 0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f, 0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e, 0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03, 0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780, 0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01, 0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0, 0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000, 0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f, 0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000, 0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00, 0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, 0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f, 0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07, 0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00, 0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0, 0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, 0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000, 0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e, 0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf, 0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f, 0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e, 0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000, 0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000, 0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0, 0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007, 0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e, 0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80, 0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000, 0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00, 0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078, 0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000, 0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e, 0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf, 0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f, 0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e, 0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000, 0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0, 0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0, 0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00, 0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007, 0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c, 0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000, 0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0, 0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0, 0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007, 0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0, 0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f, 0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007, 0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000, 0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018, 0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0, 0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00, 0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070, 0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003, 0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8, 0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80, 0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0, 0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f, 0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780, 0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff, 0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000, 0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000, 0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff, 0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000, 0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f, 0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000, 0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1, 0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f, 0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff, 0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8, 0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f, 0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780, 0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff, 0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000, 0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000, 0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff, 0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000, 0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7, 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381, 0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f, 0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3, 0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0, 0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003, 0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780, 0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff, 0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000, 0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000, 0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff, 0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000, 0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7, 0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff, 0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0, 0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007, 0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1, 0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0, 0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, 0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000, 0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000, 0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000, 0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e, 0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0, 0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f, 0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800, 0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0, 0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000, 0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000, 0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000, 0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000, 0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0, 0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000, 0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0, 0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0, 0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000, 0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff, 0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000, 0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0, 0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f, 0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, 0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0, 0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0, 0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0, 0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0, 0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0, 0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000, 0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0, 0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 }; // Definition of a 40x38 'danger' color logo const unsigned char logo40x38[4576] = { 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200, 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0, 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200, 1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0, 2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255, 255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189, 189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189, 189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123, 22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200, 1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0, 0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1, 123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189, 189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255, 0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189, 189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,255, 0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,123, 123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,1,189, 189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,255,255, 0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,1,189,189, 189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,255,255,0,1, 0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,123,0,26,255, 255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,0,4,123,123, 123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,123,123,123,86, 200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 }; // Display a warning message. inline void warn(const char *format,...) { if (cimg::exception_mode()>=1) { std::va_list ap; va_start(ap,format); std::fprintf(stderr,"\n "); std::vfprintf(stderr,format,ap); std::fputc('\n',stderr); va_end(ap); } } inline int xln(const int x) { return x>0?(int)(1+std::log10((double)x)):1; } inline char uncase(const char x) { return (char)((x<'A'||x>'Z')?x:x-'A'+'a'); } inline float atof(const char *str) { float x = 0,y = 1; if (!str) return 0; else { std::sscanf(str,"%g/%g",&x,&y); return x/y; } } inline int strlen(const char *s) { if (s) { int k; for (k=0; s[k]; ++k) ; return k; } return -1; } inline int strncmp(const char *s1, const char *s2, const int l) { if (s1 && s2) { int n = 0; for (int k=0; k=0 && s[l]!=c; --l) ; return l; } return -1; } inline const char* basename(const char *s) { return (cimg_OS!=2)?(s?s+1+cimg::strfind(s,'/'):0):(s?s+1+cimg::strfind(s,'\\'):0); } //! Return \c false for little endian CPUs (Intel), \c true for big endian CPUs (Motorola). inline bool endian() { const int x=1; return ((unsigned char*)&x)[0]?false:true; } //! Get the value of a system timer with a millisecond precision. inline unsigned long time() { #if cimg_OS==1 struct timeval st_time; gettimeofday(&st_time,0); return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000); #elif cimg_OS==2 static SYSTEMTIME st_time; GetSystemTime(&st_time); return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour))); #else return 0; #endif } //! Sleep for a certain numbers of milliseconds. /** This function frees the CPU ressources during the sleeping time. It may be used to temporize your program properly, without wasting CPU time. \sa wait(), time(). **/ inline void sleep(const unsigned int milliseconds) { #if cimg_OS==1 struct timespec tv; tv.tv_sec = milliseconds/1000; tv.tv_nsec = (milliseconds%1000)*1000000; nanosleep(&tv,0); #elif cimg_OS==2 Sleep(milliseconds); #endif } inline unsigned int wait(const unsigned int milliseconds, unsigned long& timer) { if (!timer) timer = cimg::time(); const unsigned long current_time = cimg::time(); if (current_time>=timer+milliseconds) { timer = current_time; return 0; } const unsigned long time_diff = timer + milliseconds - current_time; timer = current_time + time_diff; cimg::sleep(time_diff); return (unsigned int)time_diff; } //! Wait for a certain number of milliseconds since the last call. /** This function is equivalent to sleep() but the waiting time is computed with regard to the last call of wait(). It may be used to temporize your program properly. \sa sleep(), time(). **/ inline unsigned int wait(const unsigned int milliseconds) { static unsigned long timer = 0; if (!timer) timer = cimg::time(); return wait(milliseconds,timer); } // Use a specific srand initialization to avoid multi-threading problems // (executed only one time for a single program). inline void srand() { static bool first_time = true; if (first_time) { std::srand(cimg::time()); unsigned char *const rand_mem = new unsigned char[1+std::rand()%2048]; std::srand((unsigned int)(std::rand() + (unsigned long)rand_mem)); delete[] rand_mem; first_time = false; } } inline const char *const filenamerand() { static char id[9] = { 0,0,0,0,0,0,0,0,0 }; cimg::srand(); for (unsigned int k=0; k<8; k++) { const int v = (int)std::rand()%3; id[k] = v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))); } return id; } inline void system(const char *command, const char *module_name=0) { #if cimg_OS==2 PROCESS_INFORMATION pi; STARTUPINFO si; std::memset(&pi, 0, sizeof(PROCESS_INFORMATION)); std::memset(&si, 0, sizeof(STARTUPINFO)); GetStartupInfo(&si); si.cb = sizeof(si); si.wShowWindow = SW_HIDE; si.dwFlags |= SW_HIDE; const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi); if (res) { WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } else #endif std::system(command); command = module_name = 0; } // Convert filename into windows-style (without spaces) #if cimg_OS==2 inline void winformat_string(char *const s) { if (s && s[0]) { char *const ns = new char[MAX_PATH]; if (GetShortPathNameA(s,ns,MAX_PATH)) std::strcpy(s,ns); } } #else inline void winformat_string(char *const) {} #endif // Return path to the "Program files/" directory (windows only). /** This function is used internally in imagemagick_path, graphicsmagick_path and medcon_path on Windows platforms **/ #if cimg_OS==2 inline const char* programfiles_path() { static char *st_programfiles_path = 0; if (!st_programfiles_path) { st_programfiles_path = new char[MAX_PATH]; std::memset(st_programfiles_path,0,MAX_PATH); // Note : in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler). #if !defined(__INTEL_COMPILER) if (!SHGetSpecialFolderPathA(0,st_programfiles_path,0x0026,false)) { const char *pfPath = getenv("PROGRAMFILES"); if (pfPath) std::strcpy(st_programfiles_path,pfPath); else std::strcpy(st_programfiles_path,"C:\\PROGRA~1"); } #else std::strcpy(st_programfiles_path,"C:\\PROGRA~1"); #endif } return st_programfiles_path; } #endif //! Return path of the ImageMagick's \c convert tool. /** If you have installed the ImageMagick package in a standard directory, this function should return the correct path of the \c convert tool used by the %CImg Library to load and save compressed image formats. Conversely, if the \c convert executable is not auto-detected by the function, you can define the macro \c cimg_imagemagick_path with the correct path of the \c convert executable, before including CImg.h in your program : \code #define cimg_imagemagick_path "/users/thatsme/local/bin/convert" #include "CImg.h" int main() { CImg<> img("my_image.jpg"); // Read a JPEG image file. return 0; } \endcode Note that non compressed image formats can be read without installing ImageMagick. \sa temporary_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(). **/ inline const char* imagemagick_path() { static char *st_imagemagick_path = 0; if (!st_imagemagick_path) { st_imagemagick_path = new char[1024]; std::memset(st_imagemagick_path,0,1024); bool path_found = false; std::FILE *file = 0; #ifdef cimg_imagemagick_path std::strncpy(st_imagemagick_path,cimg_imagemagick_path,1023); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } #endif #if cimg_OS==2 const char *pf_path = programfiles_path(); if (!path_found) { std::sprintf(st_imagemagick_path,".\\convert.exe"); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"%s\\IMAGEM~1.%u-Q\\convert.exe",pf_path,k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"%s\\IMAGEM~1.%u\\convert.exe",pf_path,k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"%s\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"%s\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",pf_path,k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u-Q\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"C:\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u-Q\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u-Q\\VISUA~1\\BIN\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_imagemagick_path,"D:\\IMAGEM~1.%u\\VISUA~1\\BIN\\convert.exe",k); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } if (!path_found) std::strcpy(st_imagemagick_path,"convert.exe"); #else if (!path_found) { std::sprintf(st_imagemagick_path,"./convert"); if ((file=std::fopen(st_imagemagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) std::strcpy(st_imagemagick_path,"convert"); #endif winformat_string(st_imagemagick_path); } return st_imagemagick_path; } //! Return path of the GraphicsMagick's \c gm tool. /** If you have installed the GraphicsMagick package in a standard directory, this function should return the correct path of the \c gm tool used by the %CImg Library to load and save compressed image formats. Conversely, if the \c gm executable is not auto-detected by the function, you can define the macro \c cimg_graphicsmagick_path with the correct path of the \c gm executable, before including CImg.h in your program : \code #define cimg_graphicsmagick_path "/users/thatsme/local/bin/gm" #include "CImg.h" int main() { CImg<> img("my_image.jpg"); // Read a JPEG image file. return 0; } \endcode Note that non compressed image formats can be read without installing ImageMagick. \sa temporary_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(). **/ inline const char* graphicsmagick_path() { static char *st_graphicsmagick_path = 0; if (!st_graphicsmagick_path) { st_graphicsmagick_path = new char[1024]; std::memset(st_graphicsmagick_path,0,1024); bool path_found = false; std::FILE *file = 0; #ifdef cimg_graphicsmagick_path std::strcpy(st_graphicsmagick_path,cimg_graphicsmagick_path); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } #endif #if cimg_OS==2 const char* pf_path = programfiles_path(); if (!path_found) { std::sprintf(st_graphicsmagick_path,".\\gm.exe"); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"%s\\GRAPHI~1.%u-Q\\gm.exe",pf_path,k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"%s\\GRAPHI~1.%u\\gm.exe",pf_path,k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"%s\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"%s\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",pf_path,k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u-Q\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"C:\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u-Q\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u-Q\\VISUA~1\\BIN\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } { for (unsigned int k=0; k<=9 && !path_found; ++k) { std::sprintf(st_graphicsmagick_path,"D:\\GRAPHI~1.%u\\VISUA~1\\BIN\\gm.exe",k); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } } if (!path_found) std::strcpy(st_graphicsmagick_path,"gm.exe"); #else if (!path_found) { std::sprintf(st_graphicsmagick_path,"./gm"); if ((file=std::fopen(st_graphicsmagick_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) std::strcpy(st_graphicsmagick_path,"gm"); #endif winformat_string(st_graphicsmagick_path); } return st_graphicsmagick_path; } //! Return path of the \c XMedcon tool. /** If you have installed the XMedcon package in a standard directory, this function should return the correct path of the \c medcon tool used by the %CIg Library to load DICOM image formats. Conversely, if the \c medcon executable is not auto-detected by the function, you can define the macro \c cimg_medcon_path with the correct path of the \c medcon executable, before including CImg.h in your program : \code #define cimg_medcon_path "/users/thatsme/local/bin/medcon" #include "CImg.h" int main() { CImg<> img("my_image.dcm"); // Read a DICOM image file. return 0; } \endcode Note that \c medcon is only needed if you want to read DICOM image formats. \sa temporary_path(), get_load_dicom(), load_dicom(). **/ inline const char* medcon_path() { static char *st_medcon_path = 0; if (!st_medcon_path) { st_medcon_path = new char[1024]; std::memset(st_medcon_path,0,1024); bool path_found = false; std::FILE *file = 0; #ifdef cimg_medcon_path std::strcpy(st_medcon_path,cimg_medcon_path); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } #endif #if cimg_OS==2 const char* pf_path = programfiles_path(); if (!path_found) { std::sprintf(st_medcon_path,".\\medcon.bat"); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) { std::sprintf(st_medcon_path,".\\medcon.exe"); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) { std::sprintf(st_medcon_path,"%s\\XMedCon\\bin\\medcon.bat",pf_path); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) { std::sprintf(st_medcon_path,"%s\\XMedCon\\bin\\medcon.exe",pf_path); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) std::strcpy(st_medcon_path,"medcon.bat"); #else if (!path_found) { std::sprintf(st_medcon_path,"./medcon"); if ((file=std::fopen(st_medcon_path,"r"))!=0) { std::fclose(file); path_found = true; } } if (!path_found) std::strcpy(st_medcon_path,"medcon"); #endif winformat_string(st_medcon_path); } return st_medcon_path; } //! Return path to store temporary files. /** If you are running on a standard Unix or Windows system, this function should return a correct path where temporary files can be stored. If such a path is not auto-detected by this function, you can define the macro \c cimg_temporary_path with a correct path, before including CImg.h in your program : \code #define cimg_temporary_path "/users/thatsme/tmp" #include "CImg.h" int main() { CImg<> img("my_image.jpg"); // Read a JPEG image file (using the defined temporay path). return 0; } \endcode A temporary path is necessary to load and save compressed image formats, using \c convert or \c medcon. \sa imagemagick_path(), get_load_imagemagick(), load_imagemagick(), save_imagemagick(), get_load_dicom(), load_dicom(). **/ inline const char* temporary_path() { #define cimg_test_temporary_path(p) \ if (!path_found) { \ std::sprintf(st_temporary_path,"%s",p); \ std::sprintf(tmp,"%s%s%s",st_temporary_path,cimg_OS==2?"\\":"/",filetmp); \ if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; } \ } static char *st_temporary_path = 0; if (!st_temporary_path) { st_temporary_path = new char[1024]; std::memset(st_temporary_path,0,1024); bool path_found = false; char tmp[1024], filetmp[512]; std::FILE *file = 0; std::sprintf(filetmp,"%s.tmp",cimg::filenamerand()); #ifdef cimg_temporary_path cimg_test_temporary_path(cimg_temporary_path); #endif char *tmpPath = getenv("TMP"); if (tmpPath==NULL) { tmpPath = getenv("TEMP"); winformat_string(tmpPath); } if (tmpPath!=NULL) cimg_test_temporary_path(tmpPath); #if cimg_OS==2 cimg_test_temporary_path("C:\\WINNT\\Temp"); cimg_test_temporary_path("C:\\WINDOWS\\Temp"); cimg_test_temporary_path("C:\\Temp"); cimg_test_temporary_path("C:"); cimg_test_temporary_path("D:\\WINNT\\Temp"); cimg_test_temporary_path("D:\\WINDOWS\\Temp"); cimg_test_temporary_path("D:\\Temp"); cimg_test_temporary_path("D:"); #else cimg_test_temporary_path("/tmp"); cimg_test_temporary_path("/var/tmp"); #endif if (!path_found) { st_temporary_path[0]='\0'; std::strcpy(tmp,filetmp); if ((file=std::fopen(tmp,"wb"))!=0) { std::fclose(file); std::remove(tmp); path_found = true; } } if (!path_found) throw CImgIOException("cimg::temporary_path() : Unable to find a temporary path accessible for writing\n" "you have to set the macro 'cimg_temporary_path' to a valid path where you have writing access :\n" "#define cimg_temporary_path \"path\" (before including 'CImg.h')"); } return st_temporary_path; } inline const char *filename_split(const char *const filename, char *const body=0) { if (!filename) { if (body) body[0]='\0'; return 0; } int l = cimg::strfind(filename,'.'); if (l>=0) { if (body) { std::strncpy(body,filename,l); body[l]='\0'; } } else { if (body) std::strcpy(body,filename); l=(int)std::strlen(filename)-1; } return filename+l+1; } inline char* filename_number(const char *const filename, const int number, const unsigned int n, char *const string) { if (!filename) { if (string) string[0]='\0'; return 0; } char format[1024],body[1024]; const char *ext = cimg::filename_split(filename,body); if (n>0) std::sprintf(format,"%s_%%.%ud.%s",body,n,ext); else std::sprintf(format,"%s_%%d.%s",body,ext); std::sprintf(string,format,number); return string; } inline std::FILE *fopen(const char *const path, const char *const mode) { if(!path || !mode) throw CImgArgumentException("cimg::fopen() : File '%s' cannot be opened with mode '%s'.", path?path:"(null)",mode?mode:"(null)"); if (path[0]=='-') return (mode[0]=='r')?stdin:stdout; std::FILE *dest = std::fopen(path,mode); if (!dest) throw CImgIOException("cimg::fopen() : File '%s' cannot be opened%s", path,mode[0]=='r'?" for reading.":(mode[0]=='w'?" for writing.":"."),path); return dest; } inline int fclose(std::FILE *file) { if (!file) warn("cimg::fclose() : Can't close (null) file"); if (!file || file==stdin || file==stdout) return 0; const int errn = std::fclose(file); if (errn!=0) warn("cimg::fclose() : Error %d during file closing",errn); return errn; } template inline int fread(T *const ptr, const unsigned int nmemb, std::FILE *stream) { if (!ptr || nmemb<=0 || !stream) throw CImgArgumentException("cimg::fread() : Can't read %u x %u bytes of file pointer '%p' in buffer '%p'", nmemb,sizeof(T),stream,ptr); const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); unsigned int toread = nmemb, alread = 0, ltoread = 0, lalread = 0; do { ltoread = (toread*sizeof(T))0); if (toread>0) warn("cimg::fread() : File reading problems, only %u/%u elements read",alread,nmemb); return alread; } template inline int fwrite(const T *ptr, const unsigned int nmemb, std::FILE *stream) { if (!ptr || !stream) throw CImgArgumentException("cimg::fwrite() : Can't write %u x %u bytes of file pointer '%p' from buffer '%p'", nmemb,sizeof(T),stream,ptr); if (nmemb<=0) return 0; const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T); unsigned int towrite = nmemb, alwrite = 0, ltowrite = 0, lalwrite = 0; do { ltowrite = (towrite*sizeof(T))0); if (towrite>0) warn("cimg::fwrite() : File writing problems, only %u/%u elements written",alwrite,nmemb); return alwrite; } // Exchange the values of variables \p a and \p b template inline void swap(T& a,T& b) { T t=a; a=b; b=t; } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2) { cimg::swap(a1,b1); cimg::swap(a2,b2); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3) { cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4) { cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5,T6& a6,T6& b6) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5,T6& a6,T6& b6, T7& a7,T7& b7) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7); } template inline void swap(T1& a1,T1& b1,T2& a2,T2& b2,T3& a3,T3& b3,T4& a4,T4& b4,T5& a5,T5& b5,T6& a6,T6& b6, T7& a7,T7& b7,T8& a8,T8& b8) { cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8); } template inline void endian_swap(T* const buffer, const unsigned int size) { switch (sizeof(T)) { case 1: break; case 2: { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) { const unsigned short val = *(--ptr); *ptr = (unsigned short)((val>>8)|((val<<8))); } } break; case 4: { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) { const unsigned int val = *(--ptr); *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24); } } break; default: { for (T* ptr = buffer+size; ptr>buffer; ) { unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T); for (int i=0; i<(int)sizeof(T)/2; ++i) cimg::swap(*(pb++),*(--pe)); } break; } } } template inline T& endian_swap(T& a) { endian_swap(&a,1); return a; } inline const char* option(const char *const name, const int argc, char **argv, const char *defaut, const char *const usage=0) { static bool first = true, visu = false; const char *res = 0; if (first) { first=false; visu = (cimg::option("-h",argc,argv,(char*)0)!=0); visu |= (cimg::option("-help",argc,argv,(char*)0)!=0); visu |= (cimg::option("--help",argc,argv,(char*)0)!=0); } if (!name && visu) { if (usage) { std::fprintf(stderr,"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal); std::fprintf(stderr," : %s",usage); std::fprintf(stderr," (%s, %s)\n\n",__DATE__,__TIME__); } if (defaut) std::fprintf(stderr,"%s\n",defaut); } if (name) { if (argc>0) { int k = 0,i; while (k inline const T rol(const T& a, const unsigned int n=1) { return n?(T)((a<>((sizeof(T)<<3)-n))):a; } template inline const T ror(const T& a, const unsigned int n=1) { return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a; } //! Return the absolute value of \p a template inline T abs(const T& a) { return a>=0?a:-a; } inline bool abs(const bool a) { return a; } inline unsigned char abs(const unsigned char a) { return a; } inline unsigned short abs(const unsigned short a) { return a; } inline unsigned int abs(const unsigned int a) { return a; } inline unsigned long abs(const unsigned long a) { return a; } inline double abs(const double a) { return std::fabs(a); } inline float abs(const float a) { return (float)std::fabs((double)a); } inline int abs(const int a) { return std::abs(a); } template inline T sqr(const T& val) { return val*val; } //! Return the minimum between \p a and \p b. template inline const T& min(const T& a, const T& b) { return a<=b?a:b; } //! Return the minimum between \p a,\p b and \a c. template inline const T& min(const T& a, const T& b, const T& c) { return cimg::min(cimg::min(a,b),c); } //! Return the minimum between \p a,\p b,\p c and \p d. template inline const T& min(const T& a, const T& b, const T& c, const T& d) { return cimg::min(cimg::min(a,b,c),d); } //! Return the maximum between \p a and \p b. template inline const T& max(const T& a, const T& b) { return a>=b?a:b; } //! Return the maximum between \p a,\p b and \p c. template inline const T& max(const T& a, const T& b, const T& c) { return cimg::max(cimg::max(a,b),c); } //! Return the maximum between \p a,\p b,\p c and \p d. template inline const T& max(const T& a, const T& b, const T& c, const T& d) { return cimg::max(cimg::max(a,b,c),d); } //! Return the sign of \p x. template inline T sign(const T& x) { return (x<0)?(T)(-1):(x==0?(T)0:(T)1); } //! Return the nearest power of 2 higher than \p x. template inline unsigned long nearest_pow2(const T& x) { unsigned long i=1; while (x>i) i<<=1; return i; } //! Return \p x modulo \p m (generic modulo). /** This modulo function accepts negative and floating-points modulo numbers \p m. **/ template inline T mod(const T& x, const T& m) { const double dx = (double)x, dm = (double)m; if (x<0) { return (T)(dm+dx+dm*std::floor(-dx/dm)); } return (T)(dx-dm*std::floor(dx/dm)); } inline int mod(const char x, const char m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const short x, const short m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const int x, const int m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const long x, const long m) { return x>=0?x%m:(x%m?m+x%m:0); } inline int mod(const unsigned char x, const unsigned char m) { return x%m; } inline int mod(const unsigned short x, const unsigned short m) { return x%m; } inline int mod(const unsigned int x, const unsigned int m) { return x%m; } inline int mod(const unsigned long x, const unsigned long m) { return x%m; } //! Return minmod(\p a,\p b). /** The operator minmod(\p a,\p b) is defined to be : - minmod(\p a,\p b) = min(\p a,\p b), if (\p a * \p b)>0. - minmod(\p a,\p b) = 0, if (\p a * \p b)<=0 **/ template inline T minmod(const T& a, const T& b) { return a*b<=0?0:(a>0?(aabsb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); } else { const double tmp = absa/absb; return (absb==0?0:absb*std::sqrt(1.0+tmp*tmp)); } } //! Print informations about %CImg environement variables. /** Printing is done on the standard error output. **/ inline void info() { char tmp[1024] = { 0 }; std::fprintf(stderr,"\n %sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags :\n\n", cimg::t_red,cimg_version/100,(cimg_version%100)/10,cimg_version%10,cimg::t_normal,__DATE__,__TIME__); std::fprintf(stderr," > CPU endianness : %s%s Endian%s\n", cimg::t_bold, cimg::endian()?"Big":"Little", cimg::t_normal); std::fprintf(stderr," > Operating System : %s%-13s%s %s('cimg_OS'=%d)%s\n", cimg::t_bold, cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"), cimg::t_normal,cimg::t_purple, cimg_OS, cimg::t_normal); #ifdef cimg_use_visualcpp6 std::fprintf(stderr," > Using Visual C++ 6.0 : %s%-13s%s %s('cimg_use_visualcpp6' defined)%s\n", cimg::t_bold,"Yes",cimg::t_normal,cimg::t_purple,cimg::t_normal); #endif std::fprintf(stderr," > Display type : %s%-13s%s %s('cimg_display_type'=%d)%s\n", cimg::t_bold, cimg_display_type==0?"No display":(cimg_display_type==1?"X11":(cimg_display_type==2?"Windows GDI":"Unknow")), cimg::t_normal,cimg::t_purple, cimg_display_type, cimg::t_normal); std::fprintf(stderr," > Color terminal : %s%-13s%s %s('cimg_color_terminal' %s)%s\n", cimg::t_bold, #ifdef cimg_color_terminal "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Debug messages : %s%-13s%s %s('cimg_debug'=%d)%s\n", cimg::t_bold, cimg_debug==0?"No":(cimg_debug==1 || cimg_debug==2?"Yes":(cimg_debug==3?"Yes+":"Unknown")), cimg::t_normal,cimg::t_purple, cimg_debug, cimg::t_normal); #if cimg_display_type==1 std::fprintf(stderr," > Using XShm for X11 : %s%-13s%s %s('cimg_use_xshm' %s)%s\n", cimg::t_bold, #ifdef cimg_use_xshm "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using XRand for X11 : %s%-13s%s %s('cimg_use_xrandr' %s)%s\n", cimg::t_bold, #ifdef cimg_use_xrandr "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); #endif std::fprintf(stderr," > Using PNG library : %s%-13s%s %s('cimg_use_png' %s)%s\n", cimg::t_bold, #ifdef cimg_use_png "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using JPEG library : %s%-13s%s %s('cimg_use_jpeg' %s)%s\n", cimg::t_bold, #ifdef cimg_use_jpeg "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using TIFF library : %s%-13s%s %s('cimg_use_tiff' %s)%s\n", cimg::t_bold, #ifdef cimg_use_tiff "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using Magick++ library : %s%-13s%s %s('cimg_use_magick' %s)%s\n", cimg::t_bold, #ifdef cimg_use_magick "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using FFTW3 library : %s%-13s%s %s('cimg_use_fftw3' %s)%s\n", cimg::t_bold, #ifdef cimg_use_fftw3 "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::fprintf(stderr," > Using LAPACK library : %s%-13s%s %s('cimg_use_lapack' %s)%s\n", cimg::t_bold, #ifdef cimg_use_lapack "Yes",cimg::t_normal,cimg::t_purple,"defined", #else "No",cimg::t_normal,cimg::t_purple,"undefined", #endif cimg::t_normal); std::sprintf(tmp,"\"%.1020s\"",cimg::imagemagick_path()); std::fprintf(stderr," > Path of ImageMagick : %s%-13s%s %s('cimg_imagemagick_path'%s)%s\n", cimg::t_bold, tmp, cimg::t_normal, #ifdef cimg_imagemagick_path cimg::t_purple,"=\""cimg_imagemagick_path"\"", #else cimg::t_purple," undefined", #endif cimg::t_normal); std::sprintf(tmp,"\"%.1020s\"",cimg::graphicsmagick_path()); std::fprintf(stderr," > Path of GraphicsMagick : %s%-13s%s %s('cimg_graphicsmagick_path'%s)%s\n", cimg::t_bold, tmp, cimg::t_normal, #ifdef cimg_graphicsmagick_path cimg::t_purple,"=\""cimg_graphicsmagick_path"\"", #else cimg::t_purple," undefined", #endif cimg::t_normal); std::sprintf(tmp,"\"%.1020s\"",cimg::medcon_path()); std::fprintf(stderr," > Path of 'medcon' : %s%-13s%s %s('cimg_medcon_path'%s)%s\n", cimg::t_bold, tmp, cimg::t_normal, #ifdef cimg_medcon_path cimg::t_purple,"=\""cimg_medcon_path"\"", #else cimg::t_purple," undefined", #endif cimg::t_normal); std::sprintf(tmp,"\"%.1020s\"",cimg::temporary_path()); std::fprintf(stderr," > Temporary path : %s%-13s%s %s('cimg_temporary_path'%s)%s\n", cimg::t_bold, tmp, cimg::t_normal, #ifdef cimg_temporary_path cimg::t_purple,"=\""cimg_temporary_path"\"", #else cimg::t_purple," undefined", #endif cimg::t_normal); std::fprintf(stderr,"\n"); } #ifdef cimg_use_lapack template inline void getrf(int& N, T *lapA, int *IPIV, int &INFO) { dgetrf_(&N,&N,lapA,&N,IPIV,&INFO); } inline void getrf(int& N, float *lapA, int *IPIV, int &INFO) { sgetrf_(&N,&N,lapA,&N,IPIV,&INFO); } template inline void getri(int& N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) { dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); } inline void getri(int& N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) { sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO); } template inline void gesvd(char& JOB, int& M, int& N, T *lapA, int& MN, T *lapS, T *lapU, T *lapV, T *WORK, int& LWORK, int& INFO) { dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); } inline void gesvd(char& JOB, int& M, int& N, float *lapA, int& MN, float *lapS, float *lapU, float *lapV, float *WORK, int& LWORK, int& INFO) { sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO); } template inline void getrs(char &TRANS, int& N, T *lapA, int *IPIV, T *lapB, int& INFO) { int one = 1; dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); } inline void getrs(char &TRANS, int& N, float *lapA, int *IPIV, float *lapB, int& INFO) { int one = 1; sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO); } template inline void syev(char &JOB, char &UPLO, int& N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) { dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); } inline void syev(char &JOB, char &UPLO, int& N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) { ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO); } #endif // End of the 'cimg' namespace } /* #------------------------------------------------ # # # # Definition of mathematical operators. # # # #------------------------------------------------ */ #ifdef cimg_use_visualcpp6 template inline CImg operator+(const CImg& img, const t& val) { return CImg(img,false)+=val; } #else template inline CImg::type> operator+(const CImg& img, const t2& val) { typedef typename cimg::superset::type restype; return CImg(img,false)+=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator+(const t& val, const CImg& img) { return img+val; } #else template inline CImg::type> operator+(const t1& val, const CImg& img) { return img+val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator+(const CImgList& list, const t& val) { return CImgList(list)+=val; } #else template inline CImgList::type> operator+(const CImgList& list, const t2& val) { typedef typename cimg::superset::type restype; return CImgList(list)+=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator+(const t& val, const CImgList& list) { return list+val; } #else template inline CImgList::type> operator+(const t1& val, const CImgList& list) { return list+val; } #endif template inline CImg::type> operator+(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type restype; return CImg(img1,false)+=img2; } template inline CImgList::type> operator+(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type restype; return CImgList(list)+=img; } template inline CImgList::type> operator+(const CImgList& list, const CImg& img) { return img+list; } template inline CImgList::type> operator+(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type restype; return CImgList(list1)+=list2; } #ifdef cimg_use_visualcpp6 template inline CImg operator-(const CImg& img, const t& val) { return CImg(img,false)-=val; } #else template inline CImg::type> operator-(const CImg& img, const t2& val) { typedef typename cimg::superset::type restype; return CImg(img,false)-=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator-(const t& val, const CImg& img) { return CImg(img.width,img.height,img.depth,img.dim,val)-=img; } #else template inline CImg::type> operator-(const t1& val, const CImg& img) { typedef typename cimg::superset::type restype; return CImg(img.width,img.height,img.depth,img.dim,(restype)val)-=img; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator-(const CImgList& list, const t& val) { return CImgList(list)-=val; } #else template inline CImgList::type> operator-(const CImgList& list, const t2& val) { typedef typename cimg::superset::type restype; return CImgList(list)-=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator-(const t& val, const CImgList& list) { CImgList res(list.size); cimglist_for(res,l) res[l] = val-list[l]; return res; } #else template inline CImgList::type> operator-(const t1& val, const CImgList& list) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = val-list[l]; return res; } #endif template inline CImg::type> operator-(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type restype; return CImg(img1,false)-=img2; } template inline CImgList::type> operator-(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = img-list[l]; return res; } template inline CImgList::type> operator-(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type restype; return CImgList(list)-=img; } template inline CImgList::type> operator-(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type restype; return CImgList(list1)-=list2; } #ifdef cimg_use_visualcpp6 template inline CImg operator*(const CImg& img, const double val) { return CImg(img,false)*=val; } #else template inline CImg::type> operator*(const CImg& img, const t2& val) { typedef typename cimg::superset::type restype; return CImg(img,false)*=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator*(const double val, const CImg& img) { return img*val; } #else template inline CImg::type> operator*(const t1& val, const CImg& img) { return img*val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator*(const CImgList& list, const double val) { return CImgList(list)*=val; } #else template inline CImgList::type> operator*(const CImgList& list, const t2& val) { typedef typename cimg::superset::type restype; return CImgList(list)*=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator*(const double val, const CImgList& list) { return list*val; } #else template inline CImgList::type> operator*(const t1& val, const CImgList& list) { return list*val; } #endif template inline CImg::type> operator*(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type restype; if (img1.width!=img2.height) throw CImgArgumentException("operator*() : can't multiply a matrix (%ux%u) by a matrix (%ux%u)", img1.width,img1.height,img2.width,img2.height); CImg res(img2.width,img1.height); restype val; cimg_forXY(res,i,j) { val = 0; cimg_forX(img1,k) val+=img1(k,j)*img2(i,k); res(i,j) = val; } return res; } template inline CImgList::type> operator*(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = img*list[l]; return res; } template inline CImgList::type> operator*(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = list[l]*img; return res; } template inline CImgList::type> operator*(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type restype; CImgList res(cimg::min(list1.size,list2.size)); cimglist_for(res,l) res[l] = list1[l]*list2[l]; return res; } #ifdef cimg_use_visualcpp6 template inline CImg operator/(const CImg& img, const double val) { return CImg(img,false)/=val; } #else template inline CImg::type> operator/(const CImg& img, const t2& val) { typedef typename cimg::superset::type restype; return CImg(img,false)/=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImg operator/(const double val, CImg& img) { return val*img.get_inverse(); } #else template inline CImg::type> operator/(const t1& val, CImg& img) { return val*img.get_inverse(); } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator/(const CImgList& list, const double val) { return CImgList(list)/=val; } #else template inline CImgList::type> operator/(const CImgList& list, const t2& val) { typedef typename cimg::superset::type restype; return CImgList(list)/=val; } #endif #ifdef cimg_use_visualcpp6 template inline CImgList operator/(const double val, const CImgList& list) { CImgList res(list.size); cimglist_for(res,l) res[l] = val/list[l]; return res; } #else template inline CImgList::type> operator/(const t1& val, const CImgList& list) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = val/list[l]; return res; } #endif template inline CImg::type> operator/(const CImg& img1, const CImg& img2) { typedef typename cimg::superset::type restype; return CImg(img1,false)*=img2.get_inverse(); } template inline CImg::type> operator/(const CImg& img, const CImgList& list) { typedef typename cimg::superset::type restype; CImgList res(list.size); cimglist_for(res,l) res[l] = img/list[l]; return res; } template inline CImgList::type> operator/(const CImgList& list, const CImg& img) { typedef typename cimg::superset::type restype; return CImgList(list)/=img; } template inline CImgList::type> operator/(const CImgList& list1, const CImgList& list2) { typedef typename cimg::superset::type restype; return CImgList(list1)/=list2; } /* #------------------------------------------------ # # # # Definition of external functions. # # # #------------------------------------------------ */ template inline CImg apply(const CImg& instance, t& func) { return instance.get_apply(func); } template inline CImg::type> mul(const CImg& instance, const CImg& img) { return instance.get_mul(img); } template inline CImg::type> div(const CImg& instance, const CImg& img) { return instance.get_div(img); } template inline CImg::type> max(const CImg& instance, const CImg& img) { return instance.get_max(img); } template inline CImg max(const CImg& instance, const T& val) { return instance.get_max(val); } template inline CImg::type> min(const CImg& instance, const CImg& img) { return instance.get_min(img); } template inline CImg::type> stats(const CImg& instance) { return instance.get_stats(); } template inline CImg min(const CImg& instance, const T& val) { return instance.get_min(val); } template inline CImg::type> sqr(const CImg& instance) { return instance.get_sqr(); } template inline CImg::type> sqrt(const CImg& instance) { return instance.get_sqrt(); } template inline CImg::type> exp(const CImg& instance) { return instance.get_exp(); } template inline CImg::type> log(const CImg& instance) { return instance.get_log(); } template inline CImg::type> log10(const CImg& instance) { return instance.get_log10(); } template inline CImg::type> pow(const CImg& instance, const double p) { return instance.get_pow(p); } template inline CImg::type> pow(const CImg& instance, const CImg& img) { return instance.get_pow(img); } template inline CImg::type> abs(const CImg& instance) { return instance.get_abs(); } template inline CImg::type> cos(const CImg& instance) { return instance.get_cos(); } template inline CImg::type> sin(const CImg& instance) { return instance.get_sin(); } template inline CImg::type> tan(const CImg& instance) { return instance.get_tan(); } template inline CImg::type> acos(const CImg& instance) { return instance.get_acos(); } template inline CImg::type> asin(const CImg& instance) { return instance.get_asin(); } template inline CImg::type> atan(const CImg& instance) { return instance.get_atan(); } template inline CImg round(const CImg& instance, const float x, const unsigned int round_type=0) { return instance.get_round(x,round_type); } template inline CImg rand(const CImg& instance, const T& val_min, const T& val_max) { return instance.get_rand(val_min,val_max); } template inline CImg fill(const CImg& instance, const T& val) { return instance.get_fill(val); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1) { return instance.get_fill(val0,val1); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2) { return instance.get_fill(val0,val1,val2); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3) { return instance.get_fill(val0,val1,val2,val3); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { return instance.get_fill(val0,val1,val2,val3,val4); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { return instance.get_fill(val0,val1,val2,val3,val4,val5); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13, const T& val14) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14); } template inline CImg fill(const CImg& instance, const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13, const T& val14, const T& val15) { return instance.get_fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14,val15); } template inline CImg fill(const CImg& instance, const int& val0, ...) { CImg res(instance,false); va_list ap; va_start(ap,val0); res.template _fill(val0,ap); va_end(ap); return res; } template inline CImg fill(const CImg& instance, const double& val0, ...) { CImg res(instance,false); va_list ap; va_start(ap,val0); res.template _fill(val0,ap); va_end(ap); return res; } template inline CImg normalize(const CImg& instance, const T& a, const T& b) { return instance.get_normalize(a,b); } template inline CImg cut(const CImg& instance, const T& a, const T& b) { return instance.get_cut(a,b); } template inline CImg quantize(const CImg& instance, const unsigned int n=256, const bool keep_range=true) { return instance.get_quantize(n,keep_range); } template inline CImg threshold(const CImg& instance, const T& thres) { return instance.get_threshold(thres); } template inline CImg rotate(const CImg& instance, const float angle, const unsigned int cond=3) { return instance.get_rotate(angle,cond); } template inline CImg rotate(const CImg& instance, const float angle, const float cx, const float cy, const float zoom=1, const unsigned int cond=3) { return instance.get_rotate(angle,cx,cy,zoom,cond); } template inline CImg resize(const CImg& instance, const int pdx=-100, const int pdy=-100, const int pdz=-100, const int pdv=-100, const int interp=1, const int border_condition=-1) { return instance.get_resize(pdx,pdy,pdz,pdv,interp,border_condition); } template inline CImg resize(const CImg& instance, const CImg& src, const int interp=1, const int border_condition=-1) { return instance.get_resize(src,interp,border_condition); } template inline CImg resize(const CImg& instance, const CImgDisplay& disp, const int interp=1, const int border_condition=-1) { return instance.get_resize(disp,interp,border_condition); } template inline CImg permute_axes(const CImg& instance, const char *permut="vxyz") { return instance.get_permute_axes(instance,permut); } template inline CImg resize_halfXY(const CImg& instance) { return instance.get_resize_halfXY(); } template inline CImg crop(const CImg& instance, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const bool border_condition=false) { return instance.get_crop(x0,y0,z0,v0,x1,y1,z1,v1,border_condition); } template inline CImg crop(const CImg& instance, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool border_condition=false) { return instance.get_crop(x0,y0,z0,x1,y1,z1,border_condition); } template inline CImg crop(const CImg& instance, const int x0, const int y0, const int x1, const int y1, const bool border_condition=false) { return instance.get_crop(x0,y0,x1,y1,border_condition); } template inline CImg crop(const CImg& instance, const int x0, const int x1, const bool border_condition=false) { return instance.get_crop(x0,x1,border_condition); } template inline CImg columns(const CImg& instance, const unsigned int x0, const unsigned int x1) { return instance.get_columns(x0,x1); } template inline CImg column(const CImg& instance, const unsigned int x0) { return instance.get_column(x0); } template inline CImg lines(const CImg& instance, const unsigned int y0, const unsigned int y1) { return instance.get_lines(y0,y1); } template inline CImg line(const CImg& instance, const unsigned int y0) { return instance.get_line(y0); } template inline CImg slices(const CImg& instance, const unsigned int z0, const unsigned int z1) { return instance.get_slices(z0,z1); } template inline CImg slice(const CImg& instance, const unsigned int z0) { return instance.get_slice(z0); } template inline CImg channels(const CImg& instance, const unsigned int v0, const unsigned int v1) { return instance.get_channels(v0,v1); } template inline CImg channel(const CImg& instance, const unsigned int v0) { return instance.get_channel(v0); } template inline CImg shared_points(CImg& instance, const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_points(x0,x1,y0,z0,v0); } template inline CImg shared_points(const CImg& instance, const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_points(x0,x1,y0,z0,v0); } template inline CImg shared_lines(CImg& instance, const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_lines(y0,y1,z0,v0); } template inline CImg shared_lines(const CImg& instance, const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_lines(y0,y1,z0,v0); } template inline CImg shared_line(CImg& instance, const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_line(y0,z0,v0); } template inline CImg shared_line(const CImg& instance, const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { return instance.get_shared_line(y0,z0,v0); } template inline CImg shared_planes(CImg& instance, const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { return instance.get_shared_planes(z0,z1,v0); } template inline CImg shared_planes(const CImg& instance, const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { return instance.get_shared_planes(z0,z1,v0); } template inline CImg shared_plane(CImg& instance, const unsigned int z0, const unsigned int v0=0) { return instance.get_shared_plane(z0,v0); } template inline CImg shared_plane(const CImg& instance, const unsigned int z0, const unsigned int v0=0) { return instance.get_shared_plane(z0,v0); } template inline CImg shared_channels(CImg& instance, const unsigned int v0, const unsigned int v1) { return instance.get_shared_channels(v0,v1); } template inline CImg shared_channels(const CImg& instance, const unsigned int v0, const unsigned int v1) { return instance.get_shared_channels(v0,v1); } template inline CImg shared_channel(CImg& instance, const unsigned int v0) { return instance.get_shared_channel(v0); } template inline CImg shared_channel(const CImg& instance, const unsigned int v0) { return instance.get_shared_channel(v0); } template inline CImg shared(CImg& instance) { return instance.get_shared(); } template inline CImg shared(const CImg& instance) { return instance.get_shared(); } template inline CImg mirror(const CImg& instance, const char axe='x') { return instance.get_mirror(axe); } template inline CImg translate(const CImg& instance, const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, const int border_condition=0) { return instance.get_translate(deltax,deltay,deltaz,deltav,border_condition); } template inline CImg projections2d(const CImg& instance, const unsigned int x0, const unsigned int y0, const unsigned int z0, const int dx=-100, const int dy=-100, const int dz=-100) { return instance.get_projections2d(x0,y0,z0,dx,dy,dz); } template inline CImg::type> histogram(const CImg& instance, const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) { return instance.get_histogram(nblevels,val_min,val_max); } template inline CImg equalize_histogram(const CImg& instance, const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) { return instance.get_equalize_histogram(nblevels,val_min,val_max); } template inline CImg::type> label_regions(const CImg& instance) { return instance.get_label_regions(); } template inline CImg::type> norm_pointwise(const CImg& instance, int norm_type=2) { return instance.get_norm_pointwise(norm_type); } template inline CImg::type> orientation_pointwise(const CImg& instance) { return instance.get_orientation_pointwise(); } template inline CImgList split(const CImg& instance, const char axe='x', const unsigned int nb=0) { return instance.get_split(axe,nb); } template inline CImg append(const CImg& instance, const CImg& img, const char axis='x', const char align='c') { return instance.get_append(img,axis,align); } template inline CImgList::type> gradientXY(const CImg& instance, const int scheme=0) { return instance.get_gradientXY(scheme); } template inline CImgList::type> gradientXYZ(const CImg& instance, const int scheme=0) { return instance.get_gradientXYZ(scheme); } template inline CImg::type> structure_tensorXY(const CImg& instance, const int scheme=1) { return instance.get_structure_tensorXY(scheme); } template inline CImg::type> structure_tensorXYZ(const CImg& instance, const int scheme=1) { return instance.get_structure_tensorXYZ(scheme); } template inline CImg::type> distance_function(const CImg& instance, const unsigned int nb_iter=100, const float band_size=0.0f, const float precision=0.5f) { return instance.get_distance_function(nb_iter,band_size,precision); } template inline CImg::type> dijkstra(const CImg& instance, const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { return instance.get_dijkstra(starting_node,ending_node,previous); } template inline CImg::type> dijkstra(const CImg& instance, const unsigned int starting_node, const unsigned int ending_node=~0U) { return instance.get_dijkstra(starting_node,ending_node); } template inline CImg RGBtoLUT(const CImg& instance, const CImg& palette, const bool dithering=true, const bool indexing=false) { return instance.get_RGBtoLUT(palette,dithering,indexing); } template inline CImg RGBtoLUT(const CImg& instance, const bool dithering=true, const bool indexing=false) { return instance.get_RGBtoLUT(dithering,indexing); } template inline CImg LUTtoRGB(const CImg& instance, const CImg& palette) { return instance.get_LUTtoRGB(palette); } template inline CImg LUTtoRGB(const CImg& instance) { return instance.get_LUTtoRGB(); } template inline CImg::type> RGBtoHSV(const CImg& instance) { return instance.get_RGBtoHSV(); } template inline CImg HSVtoRGB(const CImg& instance) { return instance.get_HSVtoRGB(); } template inline CImg::type> RGBtoHSL(const CImg& instance) { return instance.get_RGBtoHSL(); } template inline CImg HSLtoRGB(const CImg& instance) { return instance.get_HSLtoRGB(); } template inline CImg RGBtoYCbCr(const CImg& instance) { return instance.get_RGBtoYCbCr(); } template inline CImg YCbCrtoRGB(const CImg& instance) { return instance.get_YCbCrtoRGB(); } template inline CImg::type> RGBtoYUV(const CImg& instance) { return instance.get_RGBtoYUV(); } template inline CImg YUVtoRGB(const CImg& instance) { return instance.get_YUVtoRGB(); } template inline CImg::type> RGBtoXYZ(const CImg& instance) { return instance.get_RGBtoXYZ(); } template inline CImg XYZtoRGB(const CImg& instance) { return instance.get_XYZtoRGB(); } template inline CImg XYZtoLab(const CImg& instance) { return instance.get_XYZtoLab(); } template inline CImg LabtoXYZ(const CImg& instance) { return instance.get_LabtoXYZ(); } template inline CImg XYZtoxyY(const CImg& instance) { return instance.get_XYZtoxyY(); } template inline CImg xyYtoXYZ(const CImg& instance) { return instance.get_xyYtoXYZ(); } template inline CImg RGBtoLab(const CImg& instance) { return instance.get_RGBtoLab(); } template inline CImg LabtoRGB(const CImg& instance) { return instance.get_LabtoRGB(); } template inline CImg RGBtoxyY(const CImg& instance) { return instance.get_RGBtoxyY(); } template inline CImg xyYtoRGB(const CImg& instance) { return instance.get_xyYtoRGB(); } template inline CImg RGBtoBayer(const CImg& instance, const bool even_mode=true) { return instance.get_RGBtoBayer(even_mode); } template inline CImg BayertoRGB(const CImg& instance, const unsigned int interpolation_type=3, const bool even_mode=true) { return instance.get_BayertoRGB(interpolation_type,even_mode); } template inline CImg::type> correlate(const CImg& instance, const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) { return instance.get_correlate(mask,cond,weighted_correl); } template inline CImg::type> convolve(const CImg& instance, const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) { return instance.get_convolve(mask,cond,weighted_convol); } template inline CImg::type> erode(const CImg& instance, const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) { return instance.get_erode(mask,cond,weighted_erosion); } template inline CImg erode(const CImg& instance, const unsigned int n, const unsigned int cond=1) { return instance.get_erode(n,cond); } template inline CImg::type> dilate(const CImg& instance, const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) { return instance.get_dilate(mask,cond,weighted_dilatation); } template inline CImg dilate(const CImg& instance, const unsigned int n, const unsigned int cond=1) { return instance.get_dilate(n,cond); } template inline CImg noise(const CImg& instance, const double sigma=-20, const unsigned int ntype=0) { return instance.get_noise(sigma,ntype); } template inline CImg::type> deriche(const CImg& instance, const float sigma, const int order=0, const char axe='x', const bool cond=true) { return instance.get_deriche(sigma,order,axe,cond); } template inline CImg::type> blur(const CImg& instance, const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) { return instance.get_blur(sigmax,sigmay,sigmaz,cond); } template inline CImg::type> blur(const CImg& instance, const float sigma, const bool cond=true) { return instance.get_blur(sigma,cond); } template inline CImg blur_anisotropic(const CImg& instance, const CImg& G, const float amplitude=60.0f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true) { return instance.get_blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); } template inline CImg blur_anisotropic(const CImg& instance, const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) { return instance.get_blur_anisotropic(mask,amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); } template inline CImg blur_anisotropic(const CImg& instance, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) { return instance.get_blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); } template inline CImg blur_bilateral(const CImg& instance, const float sigmax, const float sigmay, const float sigmaz, const float sigmar, const int bgridx, const int bgridy, const int bgridz, const int bgridr, const bool interpolation=true) { return instance.get_blur_bilateral(sigmax,sigmay,sigmaz,sigmar,bgridx,bgridy,bgridz,bgridr,interpolation); } template inline CImg blur_bilateral(const CImg& instance, const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, const bool interpolation=true) { return instance.get_blur_bilateral(sigmas,sigmar,bgrids,bgridr,interpolation); } template inline CImgList::type> FFT(const CImg& instance, const char axe, const bool inverse=false) { return instance.get_FFT(axe,inverse); } template inline CImgList::type> FFT(const CImg& instance, const bool inverse=false) { return instance.get_FFT(inverse); } template inline CImg blur_median(const CImg& instance, const unsigned int n=3) { return instance.get_blur_median(n); } template inline CImg sharpen(const CImg& instance, const float amplitude=50.0f, const float edge=1.0f, const float alpha=0.0f, const float sigma=0.0f) { return instance.get_sharpen(amplitude,edge,alpha,sigma); } template inline CImg::type> displacement_field(const CImg& instance, const CImg& reference, const float smooth=0.1f, const float precision=1e-6f, const unsigned int nb_scale=0, const unsigned int itermax=10000) { return instance.get_displacement_field(reference,smooth,precision,nb_scale,itermax); } template inline CImg matrix(const CImg& instance) { return instance.get_matrix(); } template inline CImg tensor(const CImg& instance) { return instance.get_tensor(); } template inline CImg unroll(const CImg& instance, const char axe='x') { return instance.get_unroll(axe); } template inline CImg diagonal(const CImg& instance) { return instance.get_diagonal(); } template inline CImg identity_matrix(const CImg& instance) { return instance.get_identity_matrix(); } template inline CImg sequence(const CImg& instance, const T& a0, const T& a1) { return instance.get_sequence(a0,a1); } template inline CImg vector_at(const CImg& instance, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { return instance.get_vector_at(x,y,z); } template inline CImg matrix_at(const CImg& instance, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { return instance.get_matrix_at(x,y,z); } template inline CImg tensor_at(const CImg& instance, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { return instance.get_tensor_at(x,y,z); } template inline CImg transpose(const CImg& instance) { return instance.get_transpose(); } template inline CImg::type> inverse(const CImg& instance, const bool use_LU=true) { return instance.get_inverse(use_LU); } template inline CImg::type> pseudoinverse(const CImg& instance) { return instance.get_pseudoinverse(); } template inline CImg::type> cross(const CImg& instance, const CImg& img) { return instance.get_cross(img); } template inline CImgList::type> SVD(const CImg& instance, const bool sorting=true) { return instance.get_SVD(sorting); } template inline CImg::type> solve(const CImg& instance, const CImg& A) { return instance.get_solve(A); } template inline CImgList::type> eigen(const CImg& instance) { return instance.get_eigen(); } template inline CImgList::type> symmetric_eigen(const CImg& instance) { return instance.get_symmetric_eigen(); } template inline CImg sort(const CImg& instance, CImg& permutations, const bool increasing=true) { return instance.get_sort(permutations,increasing); } template inline CImg sort(const CImg& instance, const bool increasing=true) { return instance.get_sort(increasing); } template inline CImg permute(const CImg& instance, const CImg& permutation) { return instance.get_permute(permutation); } template inline CImg::type> coordinates(const CImg& instance, const int coords_type, CImgDisplay &disp, unsigned int *const XYZ=0, const unsigned char *const color=0) { return instance.get_coordinates(coords_type,disp,XYZ,color); } template inline CImgList::type> insert(const CImgList& instance, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { return instance.get_insert(img,pos,shared); } template inline CImgList::type> insert(const CImgList& instance, const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { return instance.get_insert(n,img,pos,shared); } template inline CImgList::type> insert(const CImgList& instance, const CImgList& list, const unsigned int pos=~0U, int shared=0) { return instance.get_insert(list,pos,shared); } template inline CImgList::type> insert(const CImgList& instance, const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const int shared=0) { return instance.insert(n,list,pos,shared); } template inline CImgList remove(const CImgList& instance, const unsigned int pos) { return instance.get_remove(pos); } template inline CImgList remove(const CImgList& instance) { return instance.get_remove(); } template inline CImgList reverse(const CImgList& instance) { return instance.get_reverse(); } template inline CImgList crop(const CImgList& instance, const unsigned int i0, const unsigned int i1, const bool shared=false) { return instance.get_crop(i0,i1,shared); } template inline CImgList crop(const CImgList& instance, const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1) { return instance.get_crop(i0,i1,x0,y0,z0,v0,x1,y1,z1,v1); } template inline CImgList crop(const CImgList& instance, const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1) { return instance.get_crop(i0,i1,x0,y0,z0,x1,y1,z1); } template inline CImgList crop(const CImgList& instance, const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int x1, const int y1) { return instance.get_crop(i0,i1,x0,y0,x1,y1); } template inline CImgList crop(const CImgList& instance, const unsigned int i0, const unsigned int i1, const int x0, const int x1) { return instance.get_crop(i0,i1,x0,x1); } template inline CImgList::type> FFT(const CImgList& instance, const char axe, const bool inverse=false) { return instance.get_FFT(axe,inverse); } template inline CImgList::type> FFT(const CImgList& instance, const bool inverse=false) { return instance.get_FFT(inverse); } template inline CImgList split(const CImgList& instance, const char axe='x') { return instance.get_split(axe); } template inline CImg append(const CImgList& instance, const char axe='x', const char align='c') { return instance.get_append(axe,align); } template inline CImgList crop_font(const CImgList& instance) { return instance.get_crop_font(); } /* #------------------------------------------- # # # # Definition of the CImgDisplay structure # # # #------------------------------------------- */ //! This class represents a window which can display \ref CImg images and handles mouse and keyboard events. /** Creating a \c CImgDisplay instance opens a window that can be used to display a \c CImg image of a \c CImgList image list inside. When a display is created, associated window events (such as mouse motion, keyboard and window size changes) are handled and can be easily detected by testing specific \c CImgDisplay data fields. See \ref cimg_displays for a complete tutorial on using the \c CImgDisplay class. **/ struct CImgDisplay { //! Width of the display unsigned int width; //! Height of the display unsigned int height; //! Normalization type used for the display unsigned int normalization; //! Range of events detected by the display unsigned int events; //! Display title char* title; //! X-pos of the display on the screen volatile int window_x; //! Y-pos of the display on the screen volatile int window_y; //! Width of the underlying window volatile unsigned int window_width; //! Height of the underlying window volatile unsigned int window_height; //! X-coordinate of the mouse pointer on the display volatile int mouse_x; //! Y-coordinate of the mouse pointer on the display volatile int mouse_y; //! Button state of the mouse volatile unsigned int buttons[512]; volatile unsigned int& button; //! Wheel state of the mouse volatile int wheel; //! Key value if pressed volatile unsigned int& key; volatile unsigned int keys[512]; //! Key value if released volatile unsigned int& released_key; volatile unsigned int released_keys[512]; //! Closed state of the window volatile bool is_closed; //! Resized state of the window volatile bool is_resized; //! Moved state of the window volatile bool is_moved; //! Event state of the window volatile bool is_event; //! Fullscreen state of the display bool is_fullscreen; float fps_fps, min, max; unsigned long timer, fps_frames, fps_timer; #ifdef cimgdisplay_plugin #include cimgdisplay_plugin #endif //! Create an empty display window. CImgDisplay(): width(0),height(0),normalization(0),events(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), min(0),max(0) {} //! Create a display window with a specified size \p pwidth x \p height. /** \param dimw : Width of the display window. \param dimh : Height of the display window. \param title : Title of the display window. \param normalization_type : Normalization type of the display window (see CImgDisplay::normalize). \param events_type : Type of events handled by the display window. \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. A black image will be initially displayed in the display window. **/ CImgDisplay(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),events(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false), min(0),max(0) { assign(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); } //! Create a display window from an image. /** \param img : Image that will be used to create the display window. \param title : Title of the display window \param normalization_type : Normalization type of the display window. \param events_type : Type of events handled by the display window. \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. **/ template CImgDisplay(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),events(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(img,title,normalization_type,events_type,fullscreen_flag,closed_flag); } //! Create a display window from an image list. /** \param list : The list of images to display. \param title : Title of the display window \param normalization_type : Normalization type of the display window. \param events_type : Type of events handled by the display window. \param fullscreen_flag : Fullscreen mode. \param closed_flag : Initially visible mode. **/ template CImgDisplay(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false): width(0),height(0),normalization(0),events(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(list,title,normalization_type,events_type,fullscreen_flag,closed_flag); } //! Create a display window by copying another one. /** \param disp : Display window to copy. **/ CImgDisplay(const CImgDisplay& disp): width(0),height(0),normalization(0),events(0),title(0), window_x(0),window_y(0),window_width(0),window_height(0), mouse_x(0),mouse_y(0),button(*buttons),wheel(0),key(*keys),released_key(*released_keys), is_closed(true),is_resized(false),is_moved(false),is_event(false),is_fullscreen(false),min(0),max(0) { assign(disp); } //! Destructor ~CImgDisplay() { assign(); } //! Assignment operator CImgDisplay& operator=(const CImgDisplay& disp) { return assign(disp); } //! Return true is display is empty bool is_empty() const { return (!width || !height); } //! Return false if display is empty operator bool() const { return !is_empty(); } //! Return display width int dimx() const { return (int)width; } //! Return display height int dimy() const { return (int)height; } //! Return display window width int window_dimx() const { return (int)window_width; } //! Return display window height int window_dimy() const { return (int)window_height; } //! Return X-coordinate of the window int window_posx() const { return window_x; } //! Return Y-coordinate of the window int window_posy() const { return window_y; } //! Synchronized waiting function. Same as cimg::wait(). /** \see cimg::wait() **/ CImgDisplay& wait(const unsigned int milliseconds) { cimg::wait(milliseconds, timer); return *this; } //! Wait for an event occuring on the current display CImgDisplay& wait() { if (!is_empty()) wait(*this); return *this; } //! Wait for any event occuring on the display \c disp1 static void wait(CImgDisplay& disp1) { disp1.is_event = 0; while (!disp1.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1 or \c disp2 static void wait(CImgDisplay& disp1, CImgDisplay& disp2) { disp1.is_event = disp2.is_event = 0; while (!disp1.is_event && !disp2.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1, \c disp2 or \c disp3 static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) { disp1.is_event = disp2.is_event = disp3.is_event = 0; while (!disp1.is_event && !disp2.is_event && !disp3.is_event) wait_all(); } //! Wait for any event occuring either on the display \c disp1, \c disp2, \c disp3 or \c disp4 static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) { disp1.is_event = disp2.is_event = disp3.is_event = disp4.is_event = 0; while (!disp1.is_event && !disp2.is_event && !disp3.is_event && !disp4.is_event) wait_all(); } //! Return the frame per second rate float frames_per_second() { if (!fps_timer) fps_timer = cimg::time(); const float delta = (cimg::time()-fps_timer)/1000.0f; ++fps_frames; if (delta>=1.0f) { fps_fps = fps_frames/delta; fps_frames = 0; fps_timer = cimg::time(); } return fps_fps; } //! Display an image list CImgList into a display window. /** First, all images of the list are appended into a single image used for visualization, then this image is displayed in the current display window. \param list : The list of images to display. \param axe : The axe used to append the image for visualization. Can be 'x' (default),'y','z' or 'v'. \param align : Defines the relative alignment of images when displaying images of different sizes. Can be '\p c' (centered, which is the default), '\p p' (top alignment) and '\p n' (bottom aligment). \see CImg::get_append() **/ template CImgDisplay& display(const CImgList& list, const char axe='x', const char align='c') { return display(list.get_append(axe,align)); } //! Display an image CImg into a display window. template CImgDisplay& operator<<(const CImg& img) { return display(img); } //! Display an image CImg into a display window. template CImgDisplay& operator<<(const CImgList& list) { return display(list); } //! Resize a display window with the size of an image. /** \param img : Input image. \p image.width and \p image.height give the new dimensions of the display window. \param redraw : If \p true (default), the current displayed image in the display window will be bloc-interpolated to fit the new dimensions. If \p false, a black image will be drawn in the resized window. **/ template CImgDisplay& resize(const CImg& img, const bool redraw=true) { return resize(img.width,img.height,redraw); } //! Resize a display window using the size of the given display \p disp CImgDisplay& resize(const CImgDisplay& disp, const bool redraw=true) { return resize(disp.width,disp.height,redraw); } //! Resize a display window in its current size. CImgDisplay& resize(const bool redraw=true) { resize(window_width,window_height,redraw); return *this; } //! Display a 3d object template CImgDisplay& display_object3d(const tp& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) { CImg(width,height,1,3,0).display_object3d(points,primitives,colors,opacities,*this, centering,render_static,render_motion, double_sided,focale,ambient_light, display_axes,pose_matrix); return *this; } //! Display a 3D object. template CImgDisplay& display_object3d(const tp& points, const CImgList& primitives, const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0, const float opacity=1.0f) { typedef typename cimg::superset::type to; CImg(width,height,1,3,0).display_object3d(points,primitives,colors, CImg::vector((to)opacity), *this,centering,render_static,render_motion, double_sided,focale,ambient_light,display_axes,pose_matrix); return *this; } //! Toggle fullscreen mode CImgDisplay& toggle_fullscreen() { return assign(width,height,title,normalization,events,!is_fullscreen,is_closed); } // Inner routine used for fast resizing of buffer to display size. template static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs, t *ptrd, const unsigned int wd, const unsigned int hd) { unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy; float s, curr, old; s = (float)ws/wd; poffx = offx; curr = 0; for (unsigned int x=0; x=keys; --ptrs) if (*ptrs) { if (remove) *ptrs = 0; return true; } return false; } //! Test if a key has been pressed. bool is_key(const unsigned int key1, const bool remove) { for (unsigned int *ptrs=(unsigned int*)keys+512-1; ptrs>=keys; --ptrs) if (*ptrs==key1) { if (remove) *ptrs = 0; return true; } return false; } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const bool remove) { const unsigned int seq[] = { key1, key2 }; return is_key(seq,2,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const bool remove) { const unsigned int seq[] = { key1, key2, key3 }; return is_key(seq,3,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4 }; return is_key(seq,4,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5 }; return is_key(seq,5,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6 }; return is_key(seq,6,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7 }; return is_key(seq,7,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const unsigned int key8, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8 }; return is_key(seq,8,remove); } //! Test if a key sequence has been typed. bool is_key(const unsigned int key1, const unsigned int key2, const unsigned int key3, const unsigned int key4, const unsigned int key5, const unsigned int key6, const unsigned int key7, const unsigned int key8, const unsigned int key9, const bool remove) { const unsigned int seq[] = { key1, key2, key3, key4, key5, key6, key7, key8, key9 }; return is_key(seq,9,remove); } //! Test if a key sequence has been typed bool is_key(const unsigned int *const keyseq, const unsigned int N, const bool remove=true) { if (keyseq && N) { const unsigned int *const ps_end = keyseq+N-1, k = *ps_end, *const pk_end = (unsigned int*)keys+1+512-N; for (unsigned int *pk = (unsigned int*)keys; pk CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { fps_timer = 0*(unsigned long)(img.width + title + normalization_type + events_type + (int)fullscreen_flag + (int)closed_flag); return assign(0,0); } //! In-place version of the previous constructor template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { fps_timer = 0*(unsigned long)(list.size + title + normalization_type + events_type + (int)fullscreen_flag + (int)closed_flag); return assign(0,0); } //! In-place version of the previous constructor CImgDisplay& assign(const CImgDisplay &disp) { return assign(disp.width,disp.height); } //! Display an image in a window. template CImgDisplay& display(const CImg& img) { fps_timer = 0*img.width; return *this; } //! Resize window CImgDisplay& resize(const int width, const int height, const bool redraw=true) { fps_timer = 0*width*height*(int)redraw; return *this; } //! Move window CImgDisplay& move(const int posx, const int posy) { fps_timer = 0*posx*posy; return *this; } //! Move mouse pointer to a specific location CImgDisplay& set_mouse(const int posx, const int posy) { fps_timer = 0*posx*posy; return *this; } //! Hide mouse pointer CImgDisplay& hide_mouse() { return *this; } //! Show mouse pointer CImgDisplay& show_mouse() { return *this; } //! Wait for a window event in any CImg window static void wait_all() {} //! Show a closed display CImgDisplay& show() { return *this; } //! Close a visible display CImgDisplay& close() { return *this; } //! Set the window title CImgDisplay& set_title(const char *format,...) { fps_timer = 0*(unsigned long)format; return *this; } //! Re-paint image content in window CImgDisplay& paint() { return *this; } //! Render image buffer into GDI native image format template CImgDisplay& render(const CImg& img) { fps_timer = 0*img.width; return *this; } //! Take a snapshot of the display in the specified image. template const CImgDisplay& snapshot(CImg& img) const { img.assign(width,height,1,3,0); return *this; } // X11-based display //------------------- #elif cimg_display_type==1 void *data; Window window; Window background_window; XImage *image; Colormap colormap; Atom wm_delete_window, wm_delete_protocol; #ifdef cimg_use_xshm XShmSegmentInfo *shminfo; #endif static int screen_dimx() { int res = 0; if (!cimg::X11attr().display) { Display *disp = XOpenDisplay((std::getenv("DISPLAY") ? std::getenv("DISPLAY") : ":0.0")); if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimx() : Can't open X11 display"); res = DisplayWidth(disp,DefaultScreen(disp)); XCloseDisplay(disp); } else { #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width; else #endif res = DisplayWidth(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); } return res; } static int screen_dimy() { int res = 0; if (!cimg::X11attr().display) { Display *disp = XOpenDisplay((std::getenv("DISPLAY") ? std::getenv("DISPLAY") : ":0.0")); if (!disp) throw CImgDisplayException("CImgDisplay::screen_dimy() : Can't open X11 display"); res = DisplayHeight(disp,DefaultScreen(disp)); XCloseDisplay(disp); } else { #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) res = cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height; else #endif res = DisplayHeight(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)); } return res; } CImgDisplay& assign() { if (!is_empty()) { pthread_mutex_lock(cimg::X11attr().mutex); // Remove display window from event thread list unsigned int i; for (i=0; ishmaddr); shmctl(shminfo->shmid,IPC_RMID,0); delete shminfo; shminfo = 0; } else #endif XDestroyImage(image); data = 0; image = 0; if (cimg::X11attr().nb_bits==8) XFreeColormap(cimg::X11attr().display,colormap); colormap = 0; XSync(cimg::X11attr().display, False); // Reset display variables if (title) delete[] title; width = height = normalization = events = 0; is_fullscreen = (is_resized = (is_moved = (is_event = false))); is_closed = true; title = 0; window_x = window_y = window_width = window_height = mouse_x = mouse_y = wheel = 0; std::memset((void*)buttons,0,512*sizeof(unsigned int)); std::memset((void*)keys,0,512*sizeof(unsigned int)); std::memset((void*)released_keys,0,512*sizeof(unsigned int)); min = max = 0; // End event thread and close display if necessary if (!cimg::X11attr().nb_wins) { // Kill event thread pthread_cancel(*cimg::X11attr().event_thread); pthread_mutex_unlock(cimg::X11attr().mutex); pthread_join(*cimg::X11attr().event_thread,0); delete cimg::X11attr().event_thread; cimg::X11attr().event_thread = 0; pthread_mutex_destroy(cimg::X11attr().mutex); delete cimg::X11attr().mutex; cimg::X11attr().mutex = 0; XSync(cimg::X11attr().display, False); XCloseDisplay(cimg::X11attr().display); cimg::X11attr().display = 0; delete cimg::X11attr().gc; cimg::X11attr().gc = 0; } else pthread_mutex_unlock(cimg::X11attr().mutex); } return *this; } CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!dimw || !dimh) return assign(); _assign(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); min = max = 0; std::memset(data,0,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): (cimg::X11attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*width*height); return paint(); } template CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return render(nimg).paint(); } template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; const CImg img = list.get_append('x','p'), &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return render(nimg).paint(); } CImgDisplay& assign(const CImgDisplay& win) { if (!win) return assign(); _assign(win.width,win.height,win.title,win.normalization,win.events,win.is_fullscreen,win.is_closed); std::memcpy(data,win.data,(cimg::X11attr().nb_bits==8?sizeof(unsigned char): cimg::X11attr().nb_bits==16?sizeof(unsigned short): sizeof(unsigned int))*width*height); return paint(); } template CImgDisplay& display(const CImg& img) { if (is_empty()) assign(img.width,img.height); return render(img).paint(false); } CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { if (!nwidth || !nheight) return assign(); if (is_empty()) return assign(cimg::max(nwidth,0),cimg::max(nheight,0)); const unsigned int tmpdimx = (nwidth>0)?nwidth:(-nwidth*width/100), tmpdimy = (nheight>0)?nheight:(-nheight*height/100), dimx = cimg::min(tmpdimx?tmpdimx:1,(unsigned int)screen_dimx()), dimy = cimg::min(tmpdimy?tmpdimy:1,(unsigned int)screen_dimy()); const bool is_disp_different = (width!=dimx || height!=dimy), is_win_different = (window_width!=dimx || window_height!=dimy); if (is_disp_different || is_win_different) { pthread_mutex_lock(cimg::X11attr().mutex); XResizeWindow(cimg::X11attr().display,window,dimx,dimy); window_width = dimx; window_height = dimy; is_resized = false; if (is_disp_different) { switch (cimg::X11attr().nb_bits) { case 8: { unsigned char foo = 0; _resize(foo,dimx,dimy,redraw); } break; case 16: { unsigned short foo = 0; _resize(foo,dimx,dimy,redraw); } break; default: { unsigned int foo = 0; _resize(foo,dimx,dimy,redraw); } break; } width = dimx; height = dimy; } pthread_mutex_unlock(cimg::X11attr().mutex); if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); if (redraw) return paint(); } return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; show(); pthread_mutex_lock(cimg::X11attr().mutex); XMoveWindow(cimg::X11attr().display,window,posx,posy); is_moved = false; window_x = posx; window_y = posy; pthread_mutex_unlock(cimg::X11attr().mutex); return paint(); } CImgDisplay& set_mouse(const int posx, const int posy) { if (!is_closed && posx>=0 && posy>=0) { pthread_mutex_lock(cimg::X11attr().mutex); XWarpPointer(cimg::X11attr().display,None,window,0,0,0,0,posx,posy); is_moved = false; mouse_x = posx; mouse_y = posy; XSync(cimg::X11attr().display, False); pthread_mutex_unlock(cimg::X11attr().mutex); } return *this; } CImgDisplay& hide_mouse() { if (cimg::X11attr().display) { pthread_mutex_lock(cimg::X11attr().mutex); const char pix_data[8] = { 0 }; XColor col; col.red = col.green = col.blue = 0; Pixmap pix = XCreateBitmapFromData(cimg::X11attr().display,window,pix_data,8,8); Cursor cur = XCreatePixmapCursor(cimg::X11attr().display,pix,pix,&col,&col,0,0); XFreePixmap(cimg::X11attr().display,pix); XDefineCursor(cimg::X11attr().display,window,cur); pthread_mutex_unlock(cimg::X11attr().mutex); } return *this; } CImgDisplay& show_mouse() { if (cimg::X11attr().display) { pthread_mutex_lock(cimg::X11attr().mutex); XDefineCursor(cimg::X11attr().display,window,None); pthread_mutex_unlock(cimg::X11attr().mutex); } return *this; } static void wait_all() { if (cimg::X11attr().display) { pthread_mutex_lock(cimg::X11attr().mutex); bool flag = true; XEvent event; while (flag) { for (unsigned int i=0; iis_event = false; const unsigned int xevent_type = (cimg::X11attr().wins[i]->events)&3; const unsigned int emask = ((xevent_type>=1)?ExposureMask|StructureNotifyMask:0)| ((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0)| ((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); XSelectInput(cimg::X11attr().display,cimg::X11attr().wins[i]->window,emask); } XNextEvent(cimg::X11attr().display, &event); for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) { cimg::X11attr().wins[i]->_handle_events(&event); if (cimg::X11attr().wins[i]->is_event) flag = false; } } pthread_mutex_unlock(cimg::X11attr().mutex); } } CImgDisplay& show() { if (is_empty()) return *this; if (is_closed) { pthread_mutex_lock(cimg::X11attr().mutex); if (is_fullscreen) _init_fullscreen(); _map_window(); is_closed = false; pthread_mutex_unlock(cimg::X11attr().mutex); } return paint(); } CImgDisplay& close() { if (is_empty()) return *this; if (!is_closed) { pthread_mutex_lock(cimg::X11attr().mutex); if (is_fullscreen) _desinit_fullscreen(); XUnmapWindow(cimg::X11attr().display,window); window_x = window_y = -1; is_closed = true; pthread_mutex_unlock(cimg::X11attr().mutex); } return *this; } CImgDisplay& set_title(const char *format,...) { if (is_empty()) return *this; char tmp[1024] = {0}; va_list ap; va_start(ap, format); std::vsprintf(tmp,format,ap); va_end(ap); if (title) delete[] title; const int s = cimg::strlen(tmp)+1; title = new char[s]; std::memcpy(title,tmp,s*sizeof(char)); pthread_mutex_lock(cimg::X11attr().mutex); XStoreName(cimg::X11attr().display,window,tmp); pthread_mutex_unlock(cimg::X11attr().mutex); return *this; } CImgDisplay& paint(const bool wait_expose=true) { if (is_empty()) return *this; pthread_mutex_lock(cimg::X11attr().mutex); _paint(wait_expose); pthread_mutex_unlock(cimg::X11attr().mutex); return *this; } template CImgDisplay& render(const CImg& img, const bool flag8=false) { if (is_empty()) return *this; if (!img) throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", img.width,img.height,img.depth,img.dim,img.data); if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); if (cimg::X11attr().nb_bits==8 && (img.width!=width || img.height!=height)) return render(img.get_resize(width,height,1,-100,1)); if (cimg::X11attr().nb_bits==8 && !flag8 && img.dim==3) return render(img.get_RGBtoLUT(true),true); const unsigned int xymax = img.width*img.height; const T *data1 = img.data, *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; if (cimg::X11attr().blue_first) cimg::swap(data1,data3); pthread_mutex_lock(cimg::X11attr().mutex); if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { min = max = 0; switch (cimg::X11attr().nb_bits) { case 8: { _set_colormap(colormap,img.dim); unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1: for (unsigned int xy=0; xy>4); } break; default: for (unsigned int xy=0; xy>5)<<2) | (B>>6); } break; } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } } break; case 16: { unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; const unsigned int M = 248; if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy>2; *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); } else for (unsigned int xy=0; xy>2; *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3); *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3); } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } } break; default: { unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy::is_float()) min = (float)img.minmax(max); else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } } else if ((min>max) || normalization==1) min = (float)img.minmax(max); const float delta = max-min, mm = delta?delta:1.0f; switch (cimg::X11attr().nb_bits) { case 8: { _set_colormap(colormap,img.dim); unsigned char *const ndata = (img.width==width && img.height==height)?(unsigned char*)data:new unsigned char[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; switch (img.dim) { case 1: for (unsigned int xy=0; xy>4); } break; default: for (unsigned int xy=0; xy>5)<<2) | (B>>6); } break; } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned char*)data,width,height); delete[] ndata; } } break; case 16: { unsigned short *const ndata = (img.width==width && img.height==height)?(unsigned short*)data:new unsigned short[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; const unsigned int M = 248; if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy>2; *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); } else for (unsigned int xy=0; xy>2; *(ptrd++) = (G<<5) | ((unsigned char)(255*(*(data3++)-min)/mm)>>3); *(ptrd++) = ((unsigned char)(255*(*(data1++)-min)/mm)&M) | (G>>3); } if (ndata!=data) { _render_resize(ndata,img.width,img.height,(unsigned short*)data,width,height); delete[] ndata; } } break; default: { unsigned int *const ndata = (img.width==width && img.height==height)?(unsigned int*)data:new unsigned int[img.width*img.height]; unsigned char *ptrd = (unsigned char*)ndata; if (cimg::X11attr().byte_order) for (unsigned int xy=0; xy const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) img.assign(); else { img.assign(width,height,1,3); const unsigned int xymax = width*height; T *data1 = img.ptr(0,0,0,0), *data2 = img.ptr(0,0,0,1), *data3 = img.ptr(0,0,0,2); if (cimg::X11attr().blue_first) cimg::swap(data1,data3); switch (cimg::X11attr().nb_bits) { case 8: { unsigned char *ptrs = (unsigned char*)data; for (unsigned int xy=0; xy>5); *(data3++) = val1<<3; } else for (unsigned int xy=0; xy>5); *(data3++) = val0<<3; } } break; default: { unsigned char *ptrs = (unsigned char*)data; if (cimg::X11attr().byte_order) for (unsigned int xy=0; xyred_maskblue_mask) cimg::X11attr().blue_first = true; cimg::X11attr().byte_order = ImageByteOrder(cimg::X11attr().display); XFree(vinfo); pthread_mutex_lock(cimg::X11attr().mutex); cimg::X11attr().event_thread = new pthread_t; pthread_create(cimg::X11attr().event_thread,0,_events_thread,0); } else pthread_mutex_lock(cimg::X11attr().mutex); // Set display variables width = cimg::min(dimw,(unsigned int)screen_dimx()); height = cimg::min(dimh,(unsigned int)screen_dimy()); normalization = normalization_type%4; events = events_type%4; is_fullscreen = fullscreen_flag; title = tmp_title; window_x = window_y = wheel = 0; mouse_x = mouse_y = -1; std::memset((void*)buttons,0,512*sizeof(unsigned int)); std::memset((void*)keys,0,512*sizeof(unsigned int)); std::memset((void*)released_keys,0,512*sizeof(unsigned int)); is_resized = is_moved = is_event = false; is_closed = closed_flag; fps_timer = fps_frames = timer = 0; fps_fps = 0; // Create X11 window and palette (if 8bits display) if (is_fullscreen) { _init_fullscreen(); const unsigned int sx = screen_dimx(), sy = screen_dimy(); XSetWindowAttributes winattr; winattr.override_redirect = True; window = XCreateWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), (sx-width)/2,(sy-height)/2, width,height,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); } else window = XCreateSimpleWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), 0,0,width,height,2,0,0x0L); XStoreName(cimg::X11attr().display,window,title?title:" "); if (cimg::X11attr().nb_bits==8) { colormap = XCreateColormap(cimg::X11attr().display,window,DefaultVisual(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display)),AllocAll); _set_colormap(colormap,3); XSetWindowColormap(cimg::X11attr().display,window,colormap); } window_width = width; window_height = height; // Create XImage const unsigned int bufsize = width*height*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); #ifdef cimg_use_xshm shminfo = 0; if (XShmQueryExtension(cimg::X11attr().display)) { shminfo = new XShmSegmentInfo; image = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,shminfo,width,height); if (!image) { delete shminfo; shminfo = 0; } else { shminfo->shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777); if (shminfo->shmid==-1) { XDestroyImage(image); delete shminfo; shminfo = 0; } else { shminfo->shmaddr = image->data = (char*)(data = shmat(shminfo->shmid,0,0)); if (shminfo->shmaddr==(char*)-1) { shmctl(shminfo->shmid,IPC_RMID,0); XDestroyImage(image); delete shminfo; shminfo = 0; } else { shminfo->readOnly = False; cimg::X11attr().shm_enabled = true; XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); XShmAttach(cimg::X11attr().display, shminfo); XSync(cimg::X11attr().display, False); XSetErrorHandler(oldXErrorHandler); if (!cimg::X11attr().shm_enabled) { shmdt(shminfo->shmaddr); shmctl(shminfo->shmid,IPC_RMID,0); XDestroyImage(image); delete shminfo; shminfo = 0; } } } } } if (!shminfo) #endif { data = std::malloc(bufsize); image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,width,height,8,0); } if (!is_closed) _map_window(); else { window_x = window_y = cimg::type::min(); } if (events) { wm_delete_window = XInternAtom(cimg::X11attr().display, "WM_DELETE_WINDOW", False); wm_delete_protocol = XInternAtom(cimg::X11attr().display, "WM_PROTOCOLS", False); XSetWMProtocols(cimg::X11attr().display, window, &wm_delete_window, 1); if (is_fullscreen) XGrabKeyboard(cimg::X11attr().display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); } cimg::X11attr().wins[cimg::X11attr().nb_wins++]=this; pthread_mutex_unlock(cimg::X11attr().mutex); } void _map_window() { XWindowAttributes attr; XEvent event; XSelectInput(cimg::X11attr().display,window,ExposureMask | StructureNotifyMask); bool exposed = false, mapped = false; XMapRaised(cimg::X11attr().display,window); XSync(cimg::X11attr().display,False); do { XWindowEvent(cimg::X11attr().display,window,StructureNotifyMask | ExposureMask,&event); switch (event.type) { case MapNotify: mapped = true; break; case Expose: exposed = true; break; default: XSync(cimg::X11attr().display, False); cimg::sleep(10); } } while (!(exposed && mapped)); do { XGetWindowAttributes(cimg::X11attr().display, window, &attr); if (attr.map_state!=IsViewable) { XSync(cimg::X11attr().display,False); cimg::sleep(10); } } while (attr.map_state != IsViewable); window_x = attr.x; window_y = attr.y; } void _set_colormap(Colormap& colormap, const unsigned int dim) { XColor palette[256]; switch (dim) { case 1: // palette for greyscale images for (unsigned int index=0; index<256; ++index) { palette[index].pixel = index; palette[index].red = palette[index].green = palette[index].blue = (unsigned short)(index<<8); palette[index].flags = DoRed | DoGreen | DoBlue; } break; case 2: // palette for RG images for (unsigned int index=0, r=8; r<256; r+=16) for (unsigned int g=8; g<256; g+=16) { palette[index].pixel = index; palette[index].red = palette[index].blue = (unsigned short)(r<<8); palette[index].green = (unsigned short)(g<<8); palette[index++].flags = DoRed | DoGreen | DoBlue; } break; default: // palette for RGB images for (unsigned int index=0, r=16; r<256; r+=32) for (unsigned int g=16; g<256; g+=32) for (unsigned int b=32; b<256; b+=64) { palette[index].pixel = index; palette[index].red = (unsigned short)(r<<8); palette[index].green = (unsigned short)(g<<8); palette[index].blue = (unsigned short)(b<<8); palette[index++].flags = DoRed | DoGreen | DoBlue; } break; } XStoreColors(cimg::X11attr().display,colormap,palette,256); } void _paint(const bool wait_expose=true) { if (!is_closed) { if (wait_expose) { static XEvent event; event.xexpose.type = Expose; event.xexpose.serial = 0; event.xexpose.send_event = True; event.xexpose.display = cimg::X11attr().display; event.xexpose.window = window; event.xexpose.x = 0; event.xexpose.y = 0; event.xexpose.width = dimx(); event.xexpose.height = dimy(); event.xexpose.count = 0; XSendEvent(cimg::X11attr().display, window, False, 0, &event); } else { #ifdef cimg_use_xshm if (shminfo) XShmPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height,False); else #endif XPutImage(cimg::X11attr().display,window,*cimg::X11attr().gc,image,0,0,0,0,width,height); XSync(cimg::X11attr().display, False); } } } template void _resize(T foo, const unsigned int ndimx, const unsigned int ndimy, const bool redraw) { foo = 0; #ifdef cimg_use_xshm if (shminfo) { XShmSegmentInfo *nshminfo = new XShmSegmentInfo; XImage *nimage = XShmCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy); if (!nimage) { delete nshminfo; return; } else { nshminfo->shmid = shmget(IPC_PRIVATE, ndimx*ndimy*sizeof(T), IPC_CREAT | 0777); if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; } else { nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0); if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; } else { nshminfo->readOnly = False; cimg::X11attr().shm_enabled = true; XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm); XShmAttach(cimg::X11attr().display, nshminfo); XSync(cimg::X11attr().display, False); XSetErrorHandler(oldXErrorHandler); if (!cimg::X11attr().shm_enabled) { shmdt(nshminfo->shmaddr); shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; } else { T *const ndata = (T*)nimage->data; if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); XShmDetach(cimg::X11attr().display, shminfo); XDestroyImage(image); shmdt(shminfo->shmaddr); shmctl(shminfo->shmid,IPC_RMID,0); delete shminfo; shminfo = nshminfo; image = nimage; data = (void*)ndata; } } } } } else #endif { T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T)); if (redraw) _render_resize((T*)data,width,height,ndata,ndimx,ndimy); else std::memset(ndata,0,sizeof(T)*ndimx*ndimy); data = (void*)ndata; XDestroyImage(image); image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)data,ndimx,ndimy,8,0); } } void _init_fullscreen() { background_window = 0; if (is_fullscreen && !is_closed) { #ifdef cimg_use_xrandr int foo; if (XRRQueryExtension(cimg::X11attr().display,&foo,&foo)) { XRRRotations(cimg::X11attr().display, DefaultScreen(cimg::X11attr().display), &cimg::X11attr().curr_rotation); if (!cimg::X11attr().resolutions) { cimg::X11attr().resolutions = XRRSizes(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display),&foo); cimg::X11attr().nb_resolutions = (unsigned int)foo; } if (cimg::X11attr().resolutions) { cimg::X11attr().curr_resolution = 0; for (unsigned int i=0; i=width && nh>=height && nw<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].width) && nh<=(unsigned int)(cimg::X11attr().resolutions[cimg::X11attr().curr_resolution].height)) cimg::X11attr().curr_resolution = i; } if (cimg::X11attr().curr_resolution>0) { XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), cimg::X11attr().curr_resolution, cimg::X11attr().curr_rotation, CurrentTime); XRRFreeScreenConfigInfo(config); XSync(cimg::X11attr().display, False); } } } if (!cimg::X11attr().resolutions) cimg::warn("CImgDisplay::_create_window() : Xrandr extension is not supported by the X server."); #endif const unsigned int sx = screen_dimx(), sy = screen_dimy(); XSetWindowAttributes winattr; winattr.override_redirect = True; if (sx!=width || sy!=height) { background_window = XCreateWindow(cimg::X11attr().display, RootWindow(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)),0,0, sx,sy,0,0,InputOutput,CopyFromParent,CWOverrideRedirect,&winattr); const unsigned int bufsize = sx*sy*(cimg::X11attr().nb_bits==8?1:(cimg::X11attr().nb_bits==16?2:4)); void *background_data = std::malloc(bufsize); std::memset(background_data,0,bufsize); XImage *background_image = XCreateImage(cimg::X11attr().display,DefaultVisual(cimg::X11attr().display,DefaultScreen(cimg::X11attr().display)), cimg::X11attr().nb_bits,ZPixmap,0,(char*)background_data,sx,sy,8,0); XEvent event; XSelectInput(cimg::X11attr().display,background_window,StructureNotifyMask); XMapRaised(cimg::X11attr().display,background_window); do XWindowEvent(cimg::X11attr().display,background_window,StructureNotifyMask,&event); while (event.type!=MapNotify); #ifdef cimg_use_xshm if (shminfo) XShmPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy,False); else #endif XPutImage(cimg::X11attr().display,background_window,*cimg::X11attr().gc,background_image,0,0,0,0,sx,sy); XWindowAttributes attr; XGetWindowAttributes(cimg::X11attr().display, background_window, &attr); while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); XDestroyImage(background_image); } } } void _desinit_fullscreen() { if (is_fullscreen) { XUngrabKeyboard(cimg::X11attr().display,CurrentTime); #ifdef cimg_use_xrandr if (cimg::X11attr().resolutions && cimg::X11attr().curr_resolution) { XRRScreenConfiguration *config = XRRGetScreenInfo(cimg::X11attr().display, DefaultRootWindow(cimg::X11attr().display)); XRRSetScreenConfig(cimg::X11attr().display, config, DefaultRootWindow(cimg::X11attr().display), 0, cimg::X11attr().curr_rotation, CurrentTime); XRRFreeScreenConfigInfo(config); XSync(cimg::X11attr().display, False); cimg::X11attr().curr_resolution = 0; } #endif if (background_window) XDestroyWindow(cimg::X11attr().display,background_window); background_window = 0; is_fullscreen = false; } } void _handle_events(const XEvent *const pevent) { XEvent event=*pevent; switch (event.type) { case ClientMessage: if ((int)event.xclient.message_type==(int)wm_delete_protocol && (int)event.xclient.data.l[0]==(int)wm_delete_window) { XUnmapWindow(cimg::X11attr().display,window); mouse_x = mouse_y = -1; if (button) { std::memmove((void*)(buttons+1),(void*)buttons,512-1); button = 0; } if (key) { std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } if (released_key) { std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } is_closed = is_event = true; } break; case ConfigureNotify: { while (XCheckWindowEvent(cimg::X11attr().display,window,StructureNotifyMask,&event)); const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height; const int nx = event.xconfigure.x, ny = event.xconfigure.y; if (nw && nh && (nw!=window_width || nh!=window_height)) { window_width = nw; window_height = nh; mouse_x = mouse_y = -1; XResizeWindow(cimg::X11attr().display,window,window_width,window_height); is_resized = is_event = true; } if (nx!=window_x || ny!=window_y) { window_x = nx; window_y = ny; is_moved = is_event = true; } } break; case Expose: { while (XCheckWindowEvent(cimg::X11attr().display,window,ExposureMask,&event)); _paint(false); if (is_fullscreen) { XWindowAttributes attr; XGetWindowAttributes(cimg::X11attr().display, window, &attr); while (attr.map_state != IsViewable) XSync(cimg::X11attr().display, False); XSetInputFocus(cimg::X11attr().display, window, RevertToParent, CurrentTime); } } break; case ButtonPress: { do { switch (event.xbutton.button) { case 1: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=1; is_event = true; break; case 2: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=4; is_event = true; break; case 3: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button|=2; is_event = true; break; default: break; } } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonPressMask,&event)); } break; case ButtonRelease: { do { switch (event.xbutton.button) { case 1: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~1U; is_event = true; break; case 2: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~4U; is_event = true; break; case 3: std::memmove((void*)(buttons+1),(void*)buttons,512-1); button&=~2U; is_event = true; break; case 4: ++wheel; is_event = true; break; case 5: --wheel; is_event = true; break; default: break; } } while (XCheckWindowEvent(cimg::X11attr().display,window,ButtonReleaseMask,&event)); } break; case KeyPress: { char tmp; KeySym ksym; XLookupString(&event.xkey,&tmp,1,&ksym,0); if (key) std::memmove((void*)(keys+1),(void*)keys,512-1); key = (unsigned int)ksym; if (released_key) { std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = 0; } is_event = true; } break; case KeyRelease: { char tmp; KeySym ksym; XLookupString(&event.xkey,&tmp,1,&ksym,0); if (key) { std::memmove((void*)(keys+1),(void*)keys,512-1); key = 0; } if (released_key) std::memmove((void*)(released_keys+1),(void*)released_keys,512-1); released_key = (unsigned int)ksym; is_event = true; } break; case LeaveNotify: while (XCheckWindowEvent(cimg::X11attr().display,window,LeaveWindowMask,&event)); mouse_x = mouse_y =-1; is_event = true; break; case MotionNotify: while (XCheckWindowEvent(cimg::X11attr().display,window,PointerMotionMask,&event)); mouse_x = event.xmotion.x; mouse_y = event.xmotion.y; if (mouse_x<0 || mouse_y<0 || mouse_x>=dimx() || mouse_y>=dimy()) mouse_x = mouse_y = -1; is_event = true; break; } } static void* _events_thread(void *arg) { arg = 0; XEvent event; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0); for (;;) { pthread_mutex_lock(cimg::X11attr().mutex); for (unsigned int i=0; ievents)&3; const unsigned int emask = ((xevent_type>=1)?ExposureMask|StructureNotifyMask:0)| ((xevent_type>=2)?ButtonPressMask|KeyPressMask|PointerMotionMask|LeaveWindowMask:0)| ((xevent_type>=3)?ButtonReleaseMask|KeyReleaseMask:0); XSelectInput(cimg::X11attr().display,cimg::X11attr().wins[i]->window,emask); } bool event_flag = XCheckTypedEvent(cimg::X11attr().display, ClientMessage, &event); if (!event_flag) event_flag = XCheckMaskEvent(cimg::X11attr().display, ExposureMask|StructureNotifyMask|ButtonPressMask| KeyPressMask|PointerMotionMask|LeaveWindowMask|ButtonReleaseMask| KeyReleaseMask,&event); if (event_flag) { for (unsigned int i=0; iis_closed && event.xany.window==cimg::X11attr().wins[i]->window) cimg::X11attr().wins[i]->_handle_events(&event); } pthread_mutex_unlock(cimg::X11attr().mutex); pthread_testcancel(); cimg::sleep(7); } return 0; } // Windows-based display //----------------------- #elif cimg_display_type==2 CLIENTCREATESTRUCT ccs; BITMAPINFO bmi; unsigned int *data; DEVMODE curr_mode; HWND window; HWND background_window; HDC hdc; HANDLE thread; HANDLE created; HANDLE mutex; bool visible_cursor; static int screen_dimx() { DEVMODE mode; mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); return mode.dmPelsWidth; } static int screen_dimy() { DEVMODE mode; mode.dmSize = sizeof(DEVMODE); mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode); return mode.dmPelsHeight; } CImgDisplay& assign() { if (!is_empty()) { DestroyWindow(window); if (events) TerminateThread(thread,0); if (data) delete[] data; if (title) delete[] title; if (is_fullscreen) _desinit_fullscreen(); width = height = normalization = events = 0; is_fullscreen = is_resized = is_moved = is_event = false; is_closed = true; title = 0; window_x = window_y = window_width = window_height = mouse_x = mouse_y = wheel = 0; std::memset((void*)buttons,0,512*sizeof(unsigned int)); std::memset((void*)keys,0,512*sizeof(unsigned int)); std::memset((void*)released_keys,0,512*sizeof(unsigned int)); min = max = 0; } return *this; } CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!dimw || !dimh) return assign(); _assign(dimw,dimh,title,normalization_type,events_type,fullscreen_flag,closed_flag); min = max = 0; std::memset(data,0,sizeof(unsigned int)*width*height); return paint(); } template CImgDisplay& assign(const CImg& img, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!img) return assign(); CImg tmp; const CImg& nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } template CImgDisplay& assign(const CImgList& list, const char *title=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { if (!list) return assign(); CImg tmp; const CImg img = list.get_append('x','p'), &nimg = (img.depth==1)?img:(tmp=img.get_projections2d(img.width/2,img.height/2,img.depth/2)); _assign(nimg.width,nimg.height,title,normalization_type,events_type,fullscreen_flag,closed_flag); if (normalization==2) min = (float)nimg.minmax(max); return display(nimg); } CImgDisplay& assign(const CImgDisplay& win) { if (!win) return assign(); _assign(win.width,win.height,win.title,win.normalization,win.events,win.is_fullscreen,win.is_closed); std::memcpy(data,win.data,sizeof(unsigned int)*width*height); return paint(); } template CImgDisplay& display(const CImg& img) { if (is_empty()) assign(img.width,img.height); return render(img).paint(); } CImgDisplay& resize(const int nwidth, const int nheight, const bool redraw=true) { if (!nwidth || !nheight) return assign(); if (is_empty()) return assign(cimg::max(nwidth,0),cimg::max(nheight,0)); const unsigned int tmpdimx=(nwidth>0)?nwidth:(-nwidth*width/100), tmpdimy=(nheight>0)?nheight:(-nheight*height/100), dimx = cimg::min(tmpdimx?tmpdimx:1,(unsigned int)screen_dimx()), dimy = cimg::min(tmpdimy?tmpdimy:1,(unsigned int)screen_dimy()); const bool is_disp_different = (width!=dimx || height!=dimy), is_win_different = (window_width!=dimx || window_height!=dimy); if (is_disp_different || is_win_different) { RECT rect; rect.left = rect.top = 0; rect.right = dimx-1; rect.bottom = dimy-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int cwidth = rect.right-rect.left+1, cheight = rect.bottom-rect.top+1; SetWindowPos(window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); window_width = dimx; window_height = dimy; is_resized = false; if (is_disp_different) { unsigned int *ndata = new unsigned int[dimx*dimy]; if (redraw) _render_resize(data,width,height,ndata,dimx,dimy); else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy); delete[] data; data = ndata; bmi.bmiHeader.biWidth = dimx; bmi.bmiHeader.biHeight = -(int)dimy; width = dimx; height = dimy; } if (is_fullscreen) move((screen_dimx()-width)/2,(screen_dimy()-height)/2); if (redraw) return paint(); } return *this; } CImgDisplay& move(const int posx, const int posy) { if (is_empty()) return *this; if (!is_fullscreen) { RECT rect; rect.left = rect.top = 0; rect.right=window_width-1; rect.bottom=window_height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; SetWindowPos(window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER); } else SetWindowPos(window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER); window_x = posx; window_y = posy; is_moved = false; return show(); } CImgDisplay& set_mouse(const int posx, const int posy) { if (!is_closed && posx>=0 && posy>=0) { _update_window_pos(); const int res = (int)SetCursorPos(window_x+posx,window_y+posy); if (res) { mouse_x = posx; mouse_y = posy; } } return *this; } CImgDisplay& hide_mouse() { if (is_empty()) return *this; visible_cursor = false; ShowCursor(FALSE); SendMessage(window,WM_SETCURSOR,0,0); return *this; } CImgDisplay& show_mouse() { if (is_empty()) return *this; visible_cursor = true; ShowCursor(TRUE); SendMessage(window,WM_SETCURSOR,0,0); return *this; } static void wait_all() { WaitForSingleObject(cimg::Win32attr().wait_event,INFINITE); } CImgDisplay& show() { if (is_empty()) return *this; if (is_closed) { is_closed = false; if (is_fullscreen) _init_fullscreen(); ShowWindow(window,SW_SHOW); _update_window_pos(); } return paint(); } CImgDisplay& close() { if (is_empty()) return *this; if (!is_closed && !is_fullscreen) { if (is_fullscreen) _desinit_fullscreen(); ShowWindow(window,SW_HIDE); is_closed = true; window_x = window_y = 0; } return *this; } CImgDisplay& set_title(const char *format,...) { if (is_empty()) return *this; char tmp[1024] = {0}; va_list ap; va_start(ap, format); std::vsprintf(tmp,format,ap); va_end(ap); if (title) delete[] title; const int s = cimg::strlen(tmp)+1; title = new char[s]; std::memcpy(title,tmp,s*sizeof(char)); SetWindowTextA(window, tmp); return *this; } CImgDisplay& paint() { if (!is_closed) { WaitForSingleObject(mutex,INFINITE); SetDIBitsToDevice(hdc,0,0,width,height,0,0,0,height,data,&bmi,DIB_RGB_COLORS); ReleaseMutex(mutex); } return *this; } template CImgDisplay& render(const CImg& img) { if (is_empty()) return *this; if (!img) throw CImgArgumentException("CImgDisplay::_render_image() : Specified input image (%u,%u,%u,%u,%p) is empty.", img.width,img.height,img.depth,img.dim,img.data); if (img.depth!=1) return render(img.get_projections2d(img.width/2,img.height/2,img.depth/2)); const T *data1 = img.data, *data2 = (img.dim>=2)?img.ptr(0,0,0,1):data1, *data3 = (img.dim>=3)?img.ptr(0,0,0,2):data1; WaitForSingleObject(mutex,INFINITE); unsigned int *const ndata = (img.width==width && img.height==height)?data:new unsigned int[img.width*img.height], *ptrd = ndata; if (!normalization || (normalization==3 && cimg::type::string()==cimg::type::string())) { min = max = 0; for (unsigned int xy = img.width*img.height; xy>0; --xy) *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++); } else { if (normalization==3) { if (cimg::type::is_float()) min = (float)img.minmax(max); else { min = (float)cimg::type::min(); max = (float)cimg::type::max(); } } else if ((min>max) || normalization==1) min = (float)img.minmax(max); const float delta = max-min, mm = delta?delta:1.0f; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned char R = (unsigned char)(255*(*(data1++)-min)/mm), G = (unsigned char)(255*(*(data2++)-min)/mm), B = (unsigned char)(255*(*(data3++)-min)/mm); *(ptrd++) = (R<<16) | (G<<8) | (B); } } if (ndata!=data) { _render_resize(ndata,img.width,img.height,data,width,height); delete[] ndata; } ReleaseMutex(mutex); return *this; } template const CImgDisplay& snapshot(CImg& img) const { if (is_empty()) img.assign(); else { img.assign(width,height,1,3); T *data1 = img.ptr(0,0,0,0), *data2 = img.ptr(0,0,0,1), *data3 = img.ptr(0,0,0,2); unsigned int *ptrs = data; for (unsigned int xy = img.width*img.height; xy>0; --xy) { const unsigned int val = *(ptrs++); *(data1++) = (unsigned char)(val>>16); *(data2++) = (unsigned char)((val>>8)&0xFF); *(data3++) = (unsigned char)(val&0xFF); } } return *this; } CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *ptitle=0, const unsigned int normalization_type=3, const unsigned int events_type=3, const bool fullscreen_flag=false, const bool closed_flag=false) { // Allocate space for window title const int s = cimg::strlen(ptitle)+1; char *tmp_title = s?new char[s]:0; if (s) std::memcpy(tmp_title,ptitle,s*sizeof(char)); // Destroy previous window if existing if (!is_empty()) assign(); // Set display variables width = cimg::min(dimw,(unsigned int)screen_dimx()); height = cimg::min(dimh,(unsigned int)screen_dimy()); normalization = normalization_type%4; events = events_type%4; is_fullscreen = fullscreen_flag; title = tmp_title; window_x = window_y = wheel = 0; mouse_x = mouse_y = -1; std::memset((void*)buttons,0,512*sizeof(unsigned int)); std::memset((void*)keys,0,512*sizeof(unsigned int)); std::memset((void*)released_keys,0,512*sizeof(unsigned int)); is_resized = is_moved = is_event = false; is_closed = closed_flag; fps_timer = fps_frames = timer = 0; fps_fps = 0; visible_cursor = true; if (is_fullscreen) _init_fullscreen(); // Create event thread void *arg = (void*)(new void*[2]); ((void**)arg)[0]=(void*)this; ((void**)arg)[1]=(void*)title; if (events) { unsigned long ThreadID = 0; mutex = CreateMutex(0,FALSE,0); created = CreateEvent(0,FALSE,FALSE,0); thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID); WaitForSingleObject(created,INFINITE); } else _events_thread(arg); return *this; } static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) { #ifdef _WIN64 CImgDisplay* disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA); #else CImgDisplay* disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA); #endif MSG st_msg; switch(msg) { case WM_CLOSE: disp->mouse_x = disp->mouse_y = -1; disp->window_x = disp->window_y = 0; if (disp->button) { std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button = 0; } if (disp->key) { std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) { std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } disp->is_closed = true; ReleaseMutex(disp->mutex); ShowWindow(disp->window,SW_HIDE); disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); return 0; case WM_SIZE: { while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)); WaitForSingleObject(disp->mutex,INFINITE); const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam); if (nw && nh && (nw!=disp->width || nh!=disp->height)) { disp->window_width = nw; disp->window_height = nh; disp->mouse_x = disp->mouse_y = -1; disp->is_resized = disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } ReleaseMutex(disp->mutex); } break; case WM_MOVE: { while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)); WaitForSingleObject(disp->mutex,INFINITE); const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam)); if (nx!=disp->window_x || ny!=disp->window_y) { disp->window_x = nx; disp->window_y = ny; disp->is_moved = disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } ReleaseMutex(disp->mutex); } break; case WM_PAINT: disp->paint(); break; } if (disp->events>=2) switch(msg) { case WM_KEYDOWN: if (disp->key) std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = (int)wParam; if (disp->released_key) { std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = 0; } disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MOUSEMOVE: { while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)); disp->mouse_x = LOWORD(lParam); disp->mouse_y = HIWORD(lParam); if (disp->mouse_x<0 || disp->mouse_y<0 || disp->mouse_x>=disp->dimx() || disp->mouse_y>=disp->dimy()) disp->mouse_x=disp->mouse_y=-1; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } break; case WM_LBUTTONDOWN: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=1U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_RBUTTONDOWN: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=2U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MBUTTONDOWN: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button|=4U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case 0x020A: // WM_MOUSEWHEEL: disp->wheel+=(int)((short)HIWORD(wParam))/120; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); } if (disp->events>=3) switch(msg) { case WM_KEYUP: if (disp->key) { std::memmove((void*)(disp->keys+1),(void*)disp->keys,512-1); disp->key = 0; } if (disp->released_key) std::memmove((void*)(disp->released_keys+1),(void*)disp->released_keys,512-1); disp->released_key = (int)wParam; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_LBUTTONUP: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~1U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_RBUTTONUP: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~2U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_MBUTTONUP: std::memmove((void*)(disp->buttons+1),(void*)disp->buttons,512-1); disp->button&=~4U; disp->is_event = true; SetEvent(cimg::Win32attr().wait_event); break; case WM_SETCURSOR: if (disp->visible_cursor) ShowCursor(TRUE); else ShowCursor(FALSE); break; } return DefWindowProc(window,msg,wParam,lParam); } static DWORD WINAPI _events_thread(void* arg) { CImgDisplay *disp = (CImgDisplay*)(((void**)arg)[0]); const char *title = (const char*)(((void**)arg)[1]); MSG msg; delete[] (void**)arg; disp->bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); disp->bmi.bmiHeader.biWidth = disp->width; disp->bmi.bmiHeader.biHeight = -(int)disp->height; disp->bmi.bmiHeader.biPlanes = 1; disp->bmi.bmiHeader.biBitCount = 32; disp->bmi.bmiHeader.biCompression = BI_RGB; disp->bmi.bmiHeader.biSizeImage = 0; disp->bmi.bmiHeader.biXPelsPerMeter = 1; disp->bmi.bmiHeader.biYPelsPerMeter = 1; disp->bmi.bmiHeader.biClrUsed = 0; disp->bmi.bmiHeader.biClrImportant = 0; disp->data = new unsigned int[disp->width*disp->height]; if (!disp->is_fullscreen) // Normal window { RECT rect; rect.left = rect.top = 0; rect.right = disp->width-1; rect.bottom = disp->height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-disp->width)/2, border2 = rect.bottom-rect.top+1-disp->height-border1; disp->window = CreateWindowA("MDICLIENT",title?title:" ", WS_OVERLAPPEDWINDOW | (disp->is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT, disp->width + 2*border1, disp->height + border1 + border2, 0,0,0,&(disp->ccs)); if (!disp->is_closed) { GetWindowRect(disp->window,&rect); disp->window_x = rect.left + border1; disp->window_y = rect.top + border2; } else disp->window_x = disp->window_y = 0; } else // Fullscreen window { const unsigned int sx = screen_dimx(), sy = screen_dimy(); disp->window = CreateWindowA("MDICLIENT",title?title:" ", WS_POPUP | (disp->is_closed?0:WS_VISIBLE), (sx-disp->width)/2, (sy-disp->height)/2, disp->width,disp->height,0,0,0,&(disp->ccs)); disp->window_x = disp->window_y = 0; } SetForegroundWindow(disp->window); disp->hdc = GetDC(disp->window); disp->window_width = disp->width; disp->window_height = disp->height; disp->mouse_x = disp->mouse_y = -1; disp->wheel = 0; std::memset((void*)disp->buttons,0,512*sizeof(unsigned int)); std::memset((void*)disp->keys,0,512*sizeof(unsigned int)); std::memset((void*)disp->released_keys,0,512*sizeof(unsigned int)); disp->is_resized = disp->is_moved = disp->is_event = false; if (disp->events) { #ifdef _WIN64 SetWindowLongPtr(disp->window,GWLP_USERDATA,(LONG_PTR)disp); SetWindowLongPtr(disp->window,GWLP_WNDPROC,(LONG_PTR)_handle_events); #else SetWindowLong(disp->window,GWL_USERDATA,(LONG)disp); SetWindowLong(disp->window,GWL_WNDPROC,(LONG)_handle_events); #endif SetEvent(disp->created); while( GetMessage(&msg,0,0,0) ) DispatchMessage( &msg ); } return 0; } void _init_fullscreen() { background_window = 0; if (is_fullscreen && !is_closed) { DEVMODE mode; unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U; for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) { const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight; if (nw>=width && nh>=height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) { bestbpp = mode.dmBitsPerPel; ibest = imode; bw = nw; bh = nh; } } if (bestbpp) { curr_mode.dmSize = sizeof(DEVMODE); curr_mode.dmDriverExtra = 0; EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&curr_mode); EnumDisplaySettings(0,ibest,&mode); ChangeDisplaySettings(&mode,0); } else curr_mode.dmSize = 0; const unsigned int sx = screen_dimx(), sy = screen_dimy(); if (sx!=width || sy!=height) { CLIENTCREATESTRUCT background_ccs; background_window = CreateWindowA("MDICLIENT"," ",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs); SetForegroundWindow(background_window); } } else curr_mode.dmSize = 0; } void _desinit_fullscreen() { if (is_fullscreen) { if (background_window) DestroyWindow(background_window); background_window = 0; if (curr_mode.dmSize) ChangeDisplaySettings(&curr_mode,0); is_fullscreen = false; } } CImgDisplay& _update_window_pos() { if (!is_closed) { RECT rect; rect.left = rect.top = 0; rect.right = width-1; rect.bottom = height-1; AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false); const int border1 = (rect.right-rect.left+1-width)/2, border2 = rect.bottom-rect.top+1-height-border1; GetWindowRect(window,&rect); window_x = rect.left + border1; window_y = rect.top + border2; } else window_x = window_y = -1; return *this; } #endif }; /* #-------------------------------------- # # # # Definition of the CImg structure # # # #-------------------------------------- */ //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T. /** This is the main class of the %CImg Library. It declares and constructs an image, allows access to its pixel values, and is able to perform various image operations. \par Image representation A %CImg image is defined as an instance of the container \ref CImg<\c T>, which contains a regular grid of pixels, each pixel value being of type \c T. The image grid can have up to 4 dimensions : width, height, depth and number of channels. Usually, the three first dimensions are used to describe spatial coordinates (x,y,z), while the number of channels is rather used as a vector-valued dimension (it may describe the R,G,B color channels for instance). If you need a fifth dimension, you can use image lists \ref CImgList<\c T> rather than simple images \ref CImg<\c T>. Thus, the \ref CImg<\c T> class is able to represent volumetric images of vector-valued pixels, as well as images with less dimensions (1D scalar signal, 2D color images, ...). Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions. Concerning the pixel value type \c T : fully supported template types are the basic C++ types : unsigned char, char, short, unsigned int, int, unsigned long, long, float, double, ... . Typically, fast image display can be done using CImg images, while complex image processing algorithms may be rather coded using CImg or CImg images that have floating-point pixel values. The default value for the template T is \c float. Using your own template types may be possible. However, you will certainly have to define the complete set of arithmetic and logical operators for your class. \par Image structure The \ref CImg<\c T> structure contains \a five fields : - \ref width defines the number of \a columns of the image (size along the X-axis). - \ref height defines the number of \a rows of the image (size along the Y-axis). - \ref depth defines the number of \a slices of the image (size along the Z-axis). - \ref dim defines the number of \a channels of the image (size along the V-axis). - \ref data defines a \a pointer to the \a pixel \a data (of type \c T). You can access these fields publicly although it is recommended to use the dedicated functions dimx(), dimy(), dimz(), dimv() and ptr() to do so. Image dimensions are not limited to a specific range (as long as you got enough available memory). A value of \e 1 usually means that the corresponding dimension is \a flat. If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty. Empty images should not contain any pixel data and thus, will not be processed by CImg member functions (a CImgInstanceException will be thrown instead). Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage). \par Image declaration and construction Declaring an image can be done by using one of the several available constructors. Here is a list of the most used : - Construct images from arbitrary dimensions : - CImg img; declares an empty image. - CImg img(128,128); declares a 128x128 greyscale image with \c unsigned \c char pixel values. - CImg img(3,3); declares a 3x3 matrix with \c double coefficients. - CImg img(256,256,1,3); declares a 256x256x1x3 (color) image (colors are stored as an image with three channels). - CImg img(128,128,128); declares a 128x128x128 volumetric and greyscale image (with \c double pixel values). - CImg<> img(128,128,128,3); declares a 128x128x128 volumetric color image (with \c float pixels, which is the default value of the template parameter \c T). - \b Note : images pixels are not automatically initialized to 0. You may use the function \ref fill() to do it, or use the specific constructor taking 5 parameters like this : CImg<> img(128,128,128,3,0); declares a 128x128x128 volumetric color image with all pixel values to 0. - Construct images from filenames : - CImg img("image.jpg"); reads a JPEG color image from the file "image.jpg". - CImg img("analyze.hdr"); reads a volumetric image (ANALYZE7.5 format) from the file "analyze.hdr". - \b Note : You need to install ImageMagick to be able to read common compressed image formats (JPG,PNG,...) (See \ref cimg_files_io). - Construct images from C-style arrays : - CImg img(data_buffer,256,256); constructs a 256x256 greyscale image from a \c int* buffer \c data_buffer (of size 256x256=65536). - CImg img(data_buffer,256,256,1,3,false); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others). - CImg img(data_buffer,256,256,1,3,true); constructs a 256x256 color image from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels are multiplexed). The complete list of constructors can be found here. \par Most useful functions The \ref CImg<\c T> class contains a lot of functions that operates on images. Some of the most useful are : - operator()(), operator[]() : allows to access or write pixel values. - display() : displays the image in a new window. \sa CImgList, CImgDisplay, CImgException. **/ template struct CImg { //! Variable representing the width of the instance image (i.e. dimensions along the X-axis). /** \remark - Prefer using the function CImg::dimx() to get information about the width of an image. - Use function CImg::resize() to set a new width for an image. Setting directly the variable \c width would probably result in a library crash. - Empty images have \c width defined to \c 0. **/ unsigned int width; //! Variable representing the height of the instance image (i.e. dimensions along the Y-axis). /** \remark - Prefer using the function CImg::dimy() to get information about the height of an image. - Use function CImg::resize() to set a new height for an image. Setting directly the variable \c height would probably result in a library crash. - 1D signals have \c height defined to \c 1. - Empty images have \c height defined to \c 0. **/ unsigned int height; //! Variable representing the depth of the instance image (i.e. dimensions along the Z-axis). /** \remark - Prefer using the function CImg::dimz() to get information about the depth of an image. - Use function CImg::resize() to set a new depth for an image. Setting directly the variable \c depth would probably result in a library crash. - Classical 2D images have \c depth defined to \c 1. - Empty images have \c depth defined to \c 0. **/ unsigned int depth; //! Variable representing the number of channels of the instance image (i.e. dimensions along the V-axis). /** \remark - Prefer using the function CImg::dimv() to get information about the depth of an image. - Use function CImg::resize() to set a new vector dimension for an image. Setting directly the variable \c dim would probably result in a library crash. - Scalar-valued images (one value per pixel) have \c dim defined to \c 1. - Empty images have \c depth defined to \c 0. **/ unsigned int dim; //! Variable telling if pixel buffer of the instance image is shared with another one. bool is_shared; //! Pointer to the first pixel of the pixel buffer. T *data; //! Iterator type for CImg. /** \remark - An \p iterator is a T* pointer (address of a pixel value in the pixel buffer). - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. **/ typedef T* iterator; //! Const iterator type for CImg. /** \remark - A \p const_iterator is a const T* pointer (address of a pixel value in the pixel buffer). - Iterators are not directly used in %CImg functions, they have been introduced for compatibility with the STL. **/ typedef const T* const_iterator; //! Get value type typedef T value_type; //@} //--------------------------- // //! \name Plugins //@{ //--------------------------- #ifdef cimg_plugin #include cimg_plugin #endif //@} //-------------------------------------- // //! \name Constructors-Destructor-Copy //@{ //-------------------------------------- //! Default constructor. /** The default constructor creates an empty instance image. \remark - An empty image does not contain any data and has all of its dimensions \ref width, \ref height, \ref depth, \ref dim set to 0 as well as its pointer to the pixel buffer \ref data. - An empty image is non-shared. \see ~CImg(), assign(), is_empty(). **/ CImg(): width(0),height(0),depth(0),dim(0),is_shared(false),data(0) {} //! Destructor. /** The destructor destroys the instance image. \remark - Destructing an empty or shared image does nothing. - Otherwise, all memory used to store the pixel data of the instance image is freed. - When destroying a non-shared image, be sure that every shared instances of the same image are also destroyed to avoid further access to desallocated memory buffers. \see CImg(), assign(), is_empty(). **/ ~CImg() { if (data && !is_shared) delete[] data; } //! In-place version of the default constructor. /** This function replaces the instance image by an empty image. \remark - Memory used by the previous content of the instance image is freed if necessary. - If the instance image was initially shared, it is replaced by a (non-shared) empty image. - This function is useful to free memory used by an image that is not of use, but which has been created in the current code scope (i.e. not destroyed yet). \see ~CImg(), assign(), is_empty(). **/ CImg& assign() { if (data && !is_shared) delete[] data; width = height = depth = dim = 0; is_shared = false; data = 0; return *this; } //! In-place version of the default constructor. /** This function is strictly equivalent to \ref assign() and has been introduced for having a STL-compliant function name. \see assign(). **/ CImg& clear() { return assign(); } //! Default copy constructor. /** The default copy constructor creates a new instance image having same dimensions (\ref width, \ref height, \ref depth, \ref dim) and same pixel values as the input image \p img. \param img The input image to copy. \remark - If the input image \p img is non-shared or have a different template type \p t != \p T, the default copy constructor allocates a new pixel buffer and copy the pixel data of \p img into it. In this case, the pointers \ref data to the pixel buffers of the two images are different and the resulting instance image is non-shared. - If the input image \p img is shared and has the same template type \p t == \p T, the default copy constructor does not allocate a new pixel buffer and the resulting instance image shares its pixel buffer with the input image \p img, which means that modifying pixels of \p img also modifies the created instance image. - Copying an image having a different template type \p t != \p T performs a crude static cast conversion of each pixel value from type \p t to type \p T. - Copying an image having the same template type \p t == \p T is significantly faster. \see assign(const CImg< t >&), CImg(const CImg< t >&, const bool). **/ template CImg(const CImg& img):is_shared(false) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; const t *ptrs = img.data + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } CImg(const CImg& img) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = img.is_shared; if (is_shared) data = const_cast(img.data); else { data = new T[siz]; std::memcpy(data,img.data,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! In-place version of the default copy constructor. /** This function assigns a copy of the input image \p img to the current instance image. \param img The input image to copy. \remark - If the instance image is not shared, the content of the input image \p img is copied into a new buffer becoming the new pixel buffer of the instance image, while the old pixel buffer is freed if necessary. - If the instance image is shared, the content of the input image \p img is copied into the current (shared) pixel buffer of the instance image, modifying then the image referenced by the shared instance image. The instance image still remains shared. \see CImg(const CImg< t >&), operator=(const CImg< t >&). **/ template CImg& assign(const CImg& img) { return assign(img.data,img.width,img.height,img.depth,img.dim); } //! Advanced copy constructor. /** The advanced copy constructor - as the default constructor CImg(const CImg< t >&) - creates a new instance image having same dimensions \ref width, \ref height, \ref depth, \ref dim and same pixel values as the input image \p img. But it also decides if the created instance image shares its memory with the input image \p img (if the input parameter \p shared is set to \p true) or not (if the input parameter \p shared is set to \p false). \param img The input image to copy. \param shared Boolean flag that decides if the copy is shared on non-shared. \remark - It is not possible to create a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. - If a non-shared copy of the input image \p img is created, a new memory buffer is allocated for pixel data. - If a shared copy of the input image \p img is created, no extra memory is allocated and the pixel buffer of the instance image is the same as the one used by the input image \p img. \see CImg(const CImg&), assign(const CImg&, const bool). **/ template CImg(const CImg& img, const bool shared):is_shared(false) { if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared copy from a CImg<%s> image " "(different pixel types).",pixel_type(),CImg::pixel_type()); const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; data = new T[siz]; const t *ptrs = img.data+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } CImg(const CImg& img, const bool shared) { const unsigned int siz = img.size(); if (img.data && siz) { width = img.width; height = img.height; depth = img.depth; dim = img.dim; is_shared = shared; if (is_shared) data = const_cast(img.data); else { data = new T[siz]; std::memcpy(data,img.data,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! In-place version of the advanced constructor. /** This function - as the simpler function assign(const CImg< t >&) - assigns a copy of the input image \p img to the current instance image. But it also decides if the copy is shared (if the input parameter \p shared is set to \c true) or non-shared (if the input parameter \p shared is set to \c false). \param img The input image to copy. \param shared Boolean flag that decides if the copy is shared or non-shared. \remark - It is not possible to assign a shared copy if the input image \p img is empty or has a different pixel type \p t != \p T. - If a non-shared copy of the input image \p img is assigned, a new memory buffer is allocated for pixel data. - If a shared copy of the input image \p img is assigned, no extra memory is allocated and the pixel buffer of the instance image is the same as the one used by the input image \p img. \see CImg(const CImg&, const bool), assign(const CImg< t >&). **/ template CImg& assign(const CImg& img, const bool shared) { return assign(img.data,img.width,img.height,img.depth,img.dim,shared); } //! Constructs a new image with given size (\p dx,\p dy,\p dz,\p dv). /** This constructors create an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \ref dim. \remark - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the created image is empty and all has its dimensions set to 0. No memory for pixel data is then allocated. - This constructor creates only non-shared images. - Image pixels allocated by this constructor are \b not \b initialized. Use the constructor CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&) to get an image of desired size with pixels set to a particular value. \see assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int), CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&). **/ explicit CImg(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1): is_shared(false) { const unsigned int siz = dx*dy*dz*dv; if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; } else { width = height = depth = dim = 0; data = 0; } } //! In-place version of the previous constructor. /** This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. - If one of the input dimension \p dx,\p dy,\p dz or \p dv is set to 0, the instance image becomes empty and all has its dimensions set to 0. No memory for pixel data is then allocated. - Memory buffer used to store previous pixel values is freed if necessary. - If the instance image is shared, this constructor actually does nothing more than verifying that new and old image dimensions fit. - Image pixels allocated by this function are \b not \b initialized. Use the function assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int,const T&) to assign an image of desired size with pixels set to a particular value. \see CImg(), assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). **/ CImg& assign(const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { const unsigned long siz = dx*dy*dz*dv, curr_siz = size(); if (is_shared) { if (siz!=curr_siz) throw CImgArgumentException("CImg<%s>::assign() : Cannot assign image (%u,%u,%u,%u) to shared instance image (%u,%u,%u,%u,%p).", pixel_type(),dx,dy,dz,dv,width,height,depth,dim,data); } else { if (siz) { if (siz!=curr_siz) { if (data) delete[] data; data = new T[siz]; } width = dx; height = dy; depth = dz; dim = dv; } else { if (data) delete[] data; width = height = depth = dim = 0; data = 0; } } return *this; } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with pixel having a default value \p val. /** This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel values of the created instance image to \p val. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. \param val Default value for image pixels. \remark - This constructor has the same properties as CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). \see CImg(const unsigned int,const unsigned int,const unsigned int,const unsigned int). **/ CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T& val): is_shared(false) { const unsigned int siz = dx*dy*dz*dv; if (siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; fill(val); } else { width = height = depth = dim = 0; data = 0; } } //! In-place version of the previous constructor. /** This function replaces the instance image by a new image of size (\p dx,\p dy,\p dz,\p dv) with pixels of type \p T and sets all pixel values of the instance image to \p val. \param dx Desired size along the X-axis, i.e. the \ref width of the image. \param dy Desired size along the Y-axis, i.e. the \ref height of the image. \param dz Desired size along the Z-axis, i.e. the \ref depth of the image. \param dv Desired size along the V-axis, i.e. the number of image channels \p dim. \param val Default value for image pixels. \remark - This function has the same properties as assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). \see assign(const unsigned int,const unsigned int,const unsigned int,const unsigned int). **/ CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const T& val) { return assign(dx,dy,dz,dv).fill(val); } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (int version). CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const int val0, const int val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); } //! In-place version of the previous constructor. CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const int val0, const int val1, ...) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,int); return *this; } //! Construct an image with given size (\p dx,\p dy,\p dz,\p dv) and with specified pixel values (double version). CImg(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const double val0, const double val1, ...):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); } //! In-place version of the previous constructor. CImg& assign(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const double val0, const double val1, ...) { assign(dx,dy,dz,dv); _CImg_stdarg(*this,val0,val1,dx*dy*dz*dv,double); return *this; } //! Construct an image using dimensions of another image template CImg(const CImg& img, const char *const dimensions):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(img,dimensions); } //! In-place version of the previous constructor. template CImg& assign(const CImg& img, const char *const dimensions) { if (dimensions) { unsigned int siz[4] = { 0,1,1,1 }; const char *s = dimensions; char tmp[256] = { 0 }, c = 0; int val = 0; for (unsigned int k=0; k<4; ++k) { const int err = std::sscanf(s,"%[-0-9]%c",tmp,&c); if (err>=1) { const int err = std::sscanf(s,"%d",&val); if (err==1) { int val2 = val<0?-val:(c=='%'?val:-1); if (val2>=0) { val = (int)((k==0?img.width:(k==1?img.height:(k==2?img.depth:img.dim)))*val2/100); if (c!='%' && !val) val = 1; } siz[k] = val; } s+=cimg::strlen(tmp); if (c=='%') ++s; } if (!err) { if (!cimg::strncasecmp(s,"x",1)) { ++s; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"y",1)) { ++s; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"z",1)) { ++s; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"v",1)) { ++s; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"dx",2)) { s+=2; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"dy",2)) { s+=2; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"dz",2)) { s+=2; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dv",2)) { s+=2; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"dimx",4)) { s+=4; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"dimy",4)) { s+=4; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"dimz",4)) { s+=4; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dimv",4)) { s+=4; siz[k] = img.dim; } else if (!cimg::strncasecmp(s,"width",5)) { s+=5; siz[k] = img.width; } else if (!cimg::strncasecmp(s,"height",6)) { s+=6; siz[k] = img.height; } else if (!cimg::strncasecmp(s,"depth",5)) { s+=5; siz[k] = img.depth; } else if (!cimg::strncasecmp(s,"dim",3)) { s+=3; siz[k] = img.dim; } else { ++s; --k; } } } return assign(siz[0],siz[1],siz[2],siz[3]); } return assign(); } //! Construct an image using dimensions of another image, and fill it with a default value template CImg(const CImg& img, const char *const dimensions, const T& val): width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(img,dimensions).fill(val); } //! In-place version of the previous constructor. template CImg& assign(const CImg& img, const char *const dimensions, const T& val) { return assign(img,dimensions).fill(val); } //! Construct an image from an image file. /** This constructor creates an instance image by reading it from a file. \param filename Filename of the image file. \remark - The image format is deduced from the filename only by looking for the filename extension i.e. without analyzing the file itself. - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. More informations on this topic can be found in cimg_files_io. - If the filename is not found, a CImgIOException is thrown by this constructor. \see assign(const char *const), load(const char *const) **/ CImg(const char *const filename):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { assign(filename); } //! In-place version of the previous constructor. /** This function replaces the instance image by the one that have been read from the given file. \param filename Filename of the image file. - The image format is deduced from the filename only by looking for the filename extension i.e. without analyzing the file itself. - Recognized image formats depend on the tools installed on your system or the external libraries you use to link your code with. More informations on this topic can be found in cimg_files_io. - If the filename is not found, a CImgIOException is thrown by this constructor. **/ CImg& assign(const char *const filename) { return load(filename); } //! Construct an image from raw memory buffer. /** This constructor creates an instance image of size (\p dx,\p dy,\p dz,\p dv) and fill its pixel buffer by copying data values from the input raw pixel buffer \p data_buffer. **/ template CImg(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1, const bool shared=false):is_shared(false) { if (shared) throw CImgArgumentException("CImg<%s>::CImg() : Cannot construct a shared copy from a (%s*) buffer " "(different pixel types).",pixel_type(),CImg::pixel_type()); const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } } #ifdef cimg_use_visualcpp6 CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) { #else CImg(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1, const bool shared=false) { #endif const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; if (is_shared) data = const_cast(data_buffer); else { data = new T[siz]; std::memcpy(data,data_buffer,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } } //! In-place version of the previous constructor. #ifdef cimg_use_visualcpp6 template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv) { #else template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { #endif assign(dx,dy,dz,dv); const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) { const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; is_shared = false; data = 0; } return *this; } CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { assign(dx,dy,dz,dv); const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) std::memcpy(data,data_buffer,siz*sizeof(T)); else { width = height = depth = dim = 0; is_shared = false; data = 0; } return *this; } //! In-place version of the previous constructor, allowing to force the shared state of the instance image. template CImg& assign(const t *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) { if (shared) throw CImgArgumentException("CImg<%s>::assign() : Cannot define a shared copy from a CImg<%s> image " "(different pixel types).",pixel_type(),CImg::pixel_type()); if (data && !is_shared) delete[] data; is_shared = false; const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; data = new T[siz]; const t *ptrs = data_buffer+siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs); } else { width = height = depth = dim = 0; data = 0; } return *this; } CImg& assign(const T *const data_buffer, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dv, const bool shared) { if (data && !is_shared) delete[] data; const unsigned int siz = dx*dy*dz*dv; if (data_buffer && siz) { width = dx; height = dy; depth = dz; dim = dv; is_shared = shared; if (is_shared) data = const_cast(data_buffer); else { data = new T[siz]; std::memcpy(data,data_buffer,siz*sizeof(T)); } } else { width = height = depth = dim = 0; is_shared = false; data = 0; } return *this; } //! Construct an image from the content of a CImgDisplay instance. CImg(const CImgDisplay &disp):width(0),height(0),depth(0),dim(0),is_shared(false),data(0) { disp.snapshot(*this); } //! In-place version of the previous constructor. CImg& assign(const CImgDisplay &disp) { disp.snapshot(*this); return *this; } // INNER ROUTINE : Swap data fields of two images. CImg& swap(CImg& img) { cimg::swap(width,img.width); cimg::swap(height,img.height); cimg::swap(depth,img.depth); cimg::swap(dim,img.dim); cimg::swap(data,img.data); cimg::swap(is_shared,img.is_shared); return img; } // INNER ROUTINE : Assign *temporary* instance image '*this' to 'img'. #ifndef cimg_use_visualcpp6 CImg& assign_to(CImg& img) { if (is_shared || img.is_shared) return img.assign(*this); cimg::swap(width,img.width); cimg::swap(height,img.height); cimg::swap(depth,img.depth); cimg::swap(dim,img.dim); cimg::swap(data,img.data); return img; } #endif template CImg& assign_to(CImg& img) { return img.assign(*this); } //@} //------------------------------------- // //! \name Image Informations //@{ //------------------------------------- //! Return the type of the pixel values. /** \return a string describing the type of the image pixels (template parameter \p T). - The string returned may contains spaces ("unsigned char"). - If the template parameter T does not correspond to a registered type, the string "unknown" is returned. **/ static const char* pixel_type() { return cimg::type::string(); } //! Return the total number of pixel values in an image. /** - Equivalent to : dimx() * dimy() * dimz() * dimv(). \par example: \code CImg<> img(100,100,1,3); if (img.size()==100*100*3) std::fprintf(stderr,"This statement is true"); \endcode \sa dimx(), dimy(), dimz(), dimv() **/ unsigned long size() const { return width*height*depth*dim; } //! Return the number of columns of the instance image (size along the X-axis, i.e image width). /** \sa width, dimy(), dimz(), dimv(), size(). **/ int dimx() const { return (int)width; } //! Return the number of rows of the instance image (size along the Y-axis, i.e image height). /** \sa height, dimx(), dimz(), dimv(), size(). **/ int dimy() const { return (int)height; } //! Return the number of slices of the instance image (size along the Z-axis). /** \sa depth, dimx(), dimy(), dimv(), size(). **/ int dimz() const { return (int)depth; } //! Return the number of vector channels of the instance image (size along the V-axis). /** \sa dim, dimx(), dimy(), dimz(), size(). **/ int dimv() const { return (int)dim; } //! Return \c true if images \c (*this) and \c img have same width. template bool is_sameX(const CImg& img) const { return (width==img.width); } //! Return \c true if images \c (*this) and the display \c disp have same width. bool is_sameX(const CImgDisplay& disp) const { return (width==disp.width); } //! Return \c true if images \c (*this) and \c img have same height. template bool is_sameY(const CImg& img) const { return (height==img.height); } //! Return \c true if images \c (*this) and the display \c disp have same height. bool is_sameY(const CImgDisplay& disp) const { return (height==disp.height); } //! Return \c true if images \c (*this) and \c img have same depth. template bool is_sameZ(const CImg& img) const { return (depth==img.depth); } //! Return \c true if images \c (*this) and \c img have same dim. template bool is_sameV(const CImg& img) const { return (dim==img.dim); } //! Return \c true if images have same width and same height. template bool is_sameXY(const CImg& img) const { return (is_sameX(img) && is_sameY(img)); } //! Return \c true if image \c (*this) and the display \c disp have same width and same height. bool is_sameXY(const CImgDisplay& disp) const { return (is_sameX(disp) && is_sameY(disp)); } //! Return \c true if images have same width and same depth. template bool is_sameXZ(const CImg& img) const { return (is_sameX(img) && is_sameZ(img)); } //! Return \c true if images have same width and same number of channels. template bool is_sameXV(const CImg& img) const { return (is_sameX(img) && is_sameV(img)); } //! Return \c true if images have same height and same depth. template bool is_sameYZ(const CImg& img) const { return (is_sameY(img) && is_sameZ(img)); } //! Return \c true if images have same height and same number of channels. template bool is_sameYV(const CImg& img) const { return (is_sameY(img) && is_sameV(img)); } //! Return \c true if images have same depth and same number of channels. template bool is_sameZV(const CImg& img) const { return (is_sameZ(img) && is_sameV(img)); } //! Return \c true if images have same width, same height and same depth. template bool is_sameXYZ(const CImg& img) const { return (is_sameXY(img) && is_sameZ(img)); } //! Return \c true if images have same width, same height and same number of channels. template bool is_sameXYV(const CImg& img) const { return (is_sameXY(img) && is_sameZ(img)); } //! Return \c true if images have same width, same depth and same number of channels. template bool is_sameXZV(const CImg& img) const { return (is_sameXY(img) && is_sameZ(img)); } //! Return \c true if images have same heigth, same depth and same number of channels. template bool is_sameYZV(const CImg& img) const { return (is_sameXY(img) && is_sameZ(img)); } //! Return \c true if images \c (*this) and \c img have same width, same height, same depth and same number of channels. template bool is_sameXYZV(const CImg& img) const { return (is_sameXYZ(img) && is_sameV(img)); } //! Return \c true if pixel (x,y,z,v) is inside the image boundaries. bool contains(const int x, const int y=0, const int z=0, const int v=0) const { return data && x>=0 && x=0 && y=0 && z=0 && v bool contains(const T& pixel, t& x, t& y, t& z, t& v) const { const T *ptr = &pixel; unsigned long off = (unsigned long)(ptr-data); const unsigned long whz = width*height*depth, wh = width*height; v = (t)(off/whz); off%=whz; z =(t)(off/wh); off%=wh; y = (t)(off/width); x = (t)(off%width); return contains(x,y,z,v); } //! Return \c true if pixel is inside the image boundaries. template bool contains(const T& pixel, t& x, t& y, t& z) const { t v; return contains(pixel,x,y,z,v); } //! Return \c true if pixel is inside the image boundaries. template bool contains(const T& pixel, t& x, t& y) const { t z,v; return contains(pixel,x,y,z,v); } //! Return \c true if pixel is inside the image boundaries. template bool contains(const T& pixel, t& x) const { t y,z,v; return contains(pixel,x,y,z,v); } //! Return \c true if pixel is inside the image boundaries. template bool contains(const T& pixel) const { t x,y,z,v; return contains(pixel,x,y,z,v); } //! Return \c true if current image is empty. bool is_empty() const { return !(data && width && height && depth && dim); } //! Image to boolean conversion operator bool() const { return !is_empty(); } //! Return the offset of the pixel coordinates (\p x,\p y,\p z,\p v) with respect to the data pointer \c data. /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - No checking is done on the validity of the given coordinates. \par Example: \code CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10). float val = img[off]; // Get the blue value of the pixel. \endcode \sa ptr(), operator()(), operator[](), cimg_storage. **/ long offset(const int x=0, const int y=0, const int z=0, const int v=0) const { return x + y*width + z*width*height + v*width*height*depth; } //! Return a pointer to the pixel value located at (\p x,\p y,\p z,\p v). /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - When called without parameters, ptr() returns a pointer to the begining of the pixel buffer. - If the macro \c cimg_debug == 3, boundary checking is performed and warning messages may appear if given coordinates are outside the image range (but function performances decrease). \par example: \code CImg img(100,100,1,1,0); // Define a 100x100 greyscale image with float-valued pixels. float *ptr = ptr(10,10); // Get a pointer to the pixel located at (10,10). float val = *ptr; // Get the pixel value. \endcode \sa data, offset(), operator()(), operator[](), cimg_storage, cimg_environment. **/ T* ptr(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { const long off = offset(x,y,z,v); #if cimg_debug>=3 if (off<0 || off>=(long)size()) { cimg::warn("CImg<%s>::ptr() : Asked for a pointer at coordinates (%u,%u,%u,%u) (offset=%u), " "outside image range (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return data; } #endif return data+off; } const T* ptr(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { const long off = offset(x,y,z,v); #if cimg_debug>=3 if (off<0 || off>=(long)size()) { cimg::warn("CImg<%s>::ptr() : Trying to get a pointer at (%u,%u,%u,%u) (offset=%u) which is" "outside the data of the image (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return data; } #endif return data+off; } //! Return an iterator to the first image pixel iterator begin() { return data; } const_iterator begin() const { return data; } //! Return an iterator to the last image pixel iterator end() { return data + size(); } const_iterator end() const { return data + size(); } //! Fast access to pixel value for reading or writing. /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. - If one image dimension is equal to 1, it can be omitted in the coordinate list (see example below). - If the macro \c cimg_debug == 3, boundary checking is performed and warning messages may appear (but function performances decrease). \par example: \code CImg img(100,100,1,3,0); // Define a 100x100 color image with float-valued black pixels. const float valR = img(10,10,0,0); // Read the red component at coordinates (10,10). const float valG = img(10,10,0,1); // Read the green component at coordinates (10,10) const float valB = img(10,10,2); // Read the blue component at coordinates (10,10) (Z-coordinate omitted here). const float avg = (valR + valG + valB)/3; // Compute average pixel value. img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the pixel (10,10) by the average grey value. \endcode \sa operator[](), ptr(), offset(), cimg_storage, cimg_environment. **/ T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { const long off = offset(x,y,z,v); #if cimg_debug>=3 if (!data || off>=(long)size()) { cimg::warn("CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%u) " "outside the image range (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return *data; } #endif return data[off]; } const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { const long off = offset(x,y,z,v); #if cimg_debug>=3 if (!data || off>=(long)size()) { cimg::warn("CImg<%s>::operator() : Pixel access requested at (%u,%u,%u,%u) (offset=%u) " "outside the image range (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return *data; } #endif return data[off]; } //! Return pixel value at a given position. Equivalent to operator(). T& at(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { const long off = offset(x,y,z,v); if (!data || off>=(long)size()) throw CImgArgumentException("CImg<%s>::at() : Pixel access requested at (%u,%u,%u,%u) (offset=%u) " "outside the image range (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return data[off]; } const T& at(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { const long off = offset(x,y,z,v); if (!data || off>=(long)size()) throw CImgArgumentException("CImg<%s>::at() : Pixel access requested at (%u,%u,%u,%u) (offset=%u) " "outside the image range (%u,%u,%u,%u) (size=%u)", pixel_type(),x,y,z,v,off,width,height,depth,dim,size()); return data[off]; } //! Fast access to pixel value for reading or writing, using an offset to the image pixel. /** \param off Offset of the pixel according to the begining of the pixel buffer, given by ptr(). - If the macro \c cimg_debug==3, boundary checking is performed and warning messages may appear (but function performances decrease). - As pixel values are aligned in memory, this operator can sometime useful to access values easier than with operator()() (see example below). \par example: \code CImg vec(1,10); // Define a vector of float values (10 lines, 1 row). const float val1 = vec(0,4); // Get the fifth element using operator()(). const float val2 = vec[4]; // Get the fifth element using operator[]. Here, val2==val1. \endcode \sa operator()(), ptr(), offset(), cimg_storage, cimg_environment. **/ T& operator[](const unsigned long off) { return operator()(off); } const T& operator[](const unsigned long off) const { return operator()(off); } //! Return a reference to the last image value T& back() { return operator()(size()-1); } const T& back() const { return operator()(size()-1); } //! Return a reference to the first image value T& front() { return *data; } const T& front() const { return *data; } //! Read a pixel value with Dirichlet or Neumann boundary conditions. /** \param x X-coordinate of the pixel. \param y Y-coordinate of the pixel. \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. \param out_val Desired value if pixel coordinates are outside the image range (optional parameter). - This function allows to read pixel values with boundary checking on all coordinates. - If given coordinates are outside the image range and the parameter out_val is specified, the value \c out_val is returned. - If given coordinates are outside the image range and the parameter out_val is not specified, the closest pixel value is returned. \par example: \code CImg img(100,100,1,1,128); // Define a 100x100 images with all pixel values equal to 128. const float val1 = img.pix4d(10,10,0,0,0); // Equivalent to val1=img(10,10) (but slower). const float val2 = img.pix4d(-4,5,0,0,0); // Return 0, since coordinates are outside the image range. const float val3 = img.pix4d(10,10,5,0,64); // Return 64, since coordinates are outside the image range. \endcode \sa operator()(), linear_pix4d(), cubic_pix2d(). **/ T pix1d(const int x, const int y, const int z, const int v, const T& out_val) const { return (x<0 || x>=dimx())?out_val:(*this)(x,y,z,v); } const T& pix1d(const int x, const int y=0, const int z=0, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x),y,z,v); } //! Read a pixel value with Dirichlet or Neumann boundary conditions for the two first coordinates (\c x,\c y). T pix2d(const int x, const int y, const int z, const int v, const T& out_val) const { return (x<0 || y<0 || x>=dimx() || y>=dimy())?out_val:(*this)(x,y,z,v); } const T& pix2d(const int x, const int y, const int z=0, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y),z,v); } //! Read a pixel value with Dirichlet or Neumann boundary conditions for the three first coordinates (\c x,\c y,\c z). T pix3d(const int x, const int y, const int z, const int v, const T& out_val) const { return (x<0 || y<0 || z<0 || x>=dimx() || y>=dimy() || z>=dimz())?out_val:(*this)(x,y,z,v); } const T& pix3d(const int x, const int y, const int z, const int v=0) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z),v); } //! Read a pixel value with Dirichlet or Neumann boundary conditions. T pix4d(const int x, const int y, const int z, const int v, const T& out_val) const { return (x<0 || y<0 || z<0 || v<0 || x>=dimx() || y>=dimy() || z>=dimz() || v>=dimv())?out_val:(*this)(x,y,z,v); } T pix4d(const int x, const int y, const int z, const int v) const { return (*this)(x<0?0:(x>=dimx()?dimx()-1:x), y<0?0:(y>=dimy()?dimy()-1:y), z<0?0:(z>=dimz()?dimz()-1:z), v<0?0:(v>=dimv()?dimv()-1:v)); } //! Read a pixel value using linear interpolation for the first coordinate \c cx. /** - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the first coordinate. \sa operator()(), linear_pix4d(), linear_pix3d(), linear_pix2d(), linear_pix1d(), cubic_pix1d(). **/ typename cimg::superset::type linear_pix1d(const float fx, const int y, const int z, const int v, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), nx = x+1; const float dx = fx-x; const ftype Ic = (ftype)pix1d(x,y,z,v,out_val), In = (ftype)pix2d(nx,y,z,v,out_val); return Ic + dx*(In-Ic); } typename cimg::superset::type linear_pix1d(const float fx, const int y=0, const int z=0, const int v=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx); const unsigned int x = (unsigned int)nfx; const float dx = nfx-x; const unsigned int nx = dx>0?x+1:x; const ftype Ic = (ftype)(*this)(x,y,z,v), In = (ftype)(*this)(nx,y,z,v); return Ic + dx*(In-Ic); } //! Read a pixel value using linear interpolation for the two first coordinates (\c cx,\c cy). /** - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the two first coordinates. \sa operator()(), linear_pix4d(), linear_pix3d(), linear_pix1d(), linear_pix2d(), cubic_pix2d(). **/ typename cimg::superset::type linear_pix2d(const float fx, const float fy, const int z, const int v, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1; const float dx = fx-x, dy = fy-y; const ftype Icc = (ftype)pix2d(x,y,z,v,out_val), Inc = (ftype)pix2d(nx,y,z,v,out_val), Icn = (ftype)pix2d(x,ny,z,v,out_val), Inn = (ftype)pix2d(nx,ny,z,v,out_val); return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); } typename cimg::superset::type linear_pix2d(const float fx, const float fy, const int z=0, const int v=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy; const float dx = nfx-x, dy = nfy-y; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y; const ftype Icc = (ftype)(*this)(x,y,z,v), Inc = (ftype)(*this)(nx,y,z,v), Icn = (ftype)(*this)(x,ny,z,v), Inn = (ftype)(*this)(nx,ny,z,v); return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc); } //! Read a pixel value using linear interpolation for the three first coordinates (\c cx,\c cy,\c cz). /** - Same as linear_pix4d(), except that linear interpolation and boundary checking is performed only on the three first coordinates. \sa operator()(), linear_pix4d(), linear_pix2d(), linear_pix1d(), linear_pix3d(), cubic_pix2d(). **/ typename cimg::superset::type linear_pix3d(const float fx, const float fy, const float fz, const int v, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1, z = (int)fz-(fz>=0?0:1), nz = z+1; const float dx = fx-x, dy = fy-y, dz = fz-z; const ftype Iccc = (ftype)pix3d(x,y,z,v,out_val), Incc = (ftype)pix3d(nx,y,z,v,out_val), Icnc = (ftype)pix3d(x,ny,z,v,out_val), Innc = (ftype)pix3d(nx,ny,z,v,out_val), Iccn = (ftype)pix3d(x,y,nz,v,out_val), Incn = (ftype)pix3d(nx,y,nz,v,out_val), Icnn = (ftype)pix3d(x,ny,nz,v,out_val), Innn = (ftype)pix3d(nx,ny,nz,v,out_val); return Iccc + dx*(Incc-Iccc + dy*(Iccc+Innc-Icnc-Incc + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + dz*(Iccc+Incn-Iccn-Incc)) + dy*(Icnc-Iccc + dz*(Iccc+Icnn-Iccn-Icnc)) + dz*(Iccn-Iccc); } typename cimg::superset::type linear_pix3d(const float fx, const float fy=0, const float fz=0, const int v=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy), nfz = fz<0?0:(fz>depth-1?depth-1:fz); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy, z = (unsigned int)nfz; const float dx = nfx-x, dy = nfy-y, dz = nfz-z; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z; const ftype Iccc = (ftype)(*this)(x,y,z,v), Incc = (ftype)(*this)(nx,y,z,v), Icnc = (ftype)(*this)(x,ny,z,v), Innc = (ftype)(*this)(nx,ny,z,v), Iccn = (ftype)(*this)(x,y,nz,v), Incn = (ftype)(*this)(nx,y,nz,v), Icnn = (ftype)(*this)(x,ny,nz,v), Innn = (ftype)(*this)(nx,ny,nz,v); return Iccc + dx*(Incc-Iccc + dy*(Iccc+Innc-Icnc-Incc + dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) + dz*(Iccc+Incn-Iccn-Incc)) + dy*(Icnc-Iccc + dz*(Iccc+Icnn-Iccn-Icnc)) + dz*(Iccn-Iccc); } //! Read a pixel value using linear interpolation. /** \param fx X-coordinate of the pixel (float-valued). \param fy Y-coordinate of the pixel (float-valued). \param fz Z-coordinate of the pixel (float-valued). \param fv V-coordinate of the pixel (float-valued). \param out_val Out-of-border pixel value - This function allows to read pixel values with boundary checking on all coordinates. - If given coordinates are outside the image range, the value of the nearest pixel inside the image is returned (Neumann boundary conditions). - If given coordinates are float-valued, a linear interpolation is performed in order to compute the returned value. \par example: \code CImg img(2,2); // Define a greyscale 2x2 image. img(0,0) = 0; // Fill image with specified pixel values. img(1,0) = 1; img(0,1) = 2; img(1,1) = 3; const double val = img.linear_pix4d(0.5,0.5); // Return val=1.5, which is the average intensity of the four pixels values. \endcode \sa operator()(), linear_pix3d(), linear_pix2d(), linear_pix1d(), cubic_pix2d(). **/ typename cimg::superset::type linear_pix4d(const float fx, const float fy, const float fz, const float fv, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), nx = x+1, y = (int)fy-(fy>=0?0:1), ny = y+1, z = (int)fz-(fz>=0?0:1), nz = z+1, v = (int)fv-(fv>=0?0:1), nv = v+1; const float dx = fx-x, dy = fy-y, dz = fz-z, dv = fv-v; const ftype Icccc = (ftype)pix4d(x,y,z,v,out_val), Inccc = (ftype)pix4d(nx,y,z,v,out_val), Icncc = (ftype)pix4d(x,ny,z,v,out_val), Inncc = (ftype)pix4d(nx,ny,z,v,out_val), Iccnc = (ftype)pix4d(x,y,nz,v,out_val), Incnc = (ftype)pix4d(nx,y,nz,v,out_val), Icnnc = (ftype)pix4d(x,ny,nz,v,out_val), Innnc = (ftype)pix4d(nx,ny,nz,v,out_val), Icccn = (ftype)pix4d(x,y,z,nv,out_val), Inccn = (ftype)pix4d(nx,y,z,nv,out_val), Icncn = (ftype)pix4d(x,ny,z,nv,out_val), Inncn = (ftype)pix4d(nx,ny,z,nv,out_val), Iccnn = (ftype)pix4d(x,y,nz,nv,out_val), Incnn = (ftype)pix4d(nx,y,nz,nv,out_val), Icnnn = (ftype)pix4d(x,ny,nz,nv,out_val), Innnn = (ftype)pix4d(nx,ny,nz,nv,out_val); return Icccc + dx*(Inccc-Icccc + dy*(Icccc+Inncc-Icncc-Inccc + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + dz*(Icccc+Incnc-Iccnc-Inccc + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + dv*(Icccc+Inccn-Inccc-Icccn)) + dy*(Icncc-Icccc + dz*(Icccc+Icnnc-Iccnc-Icncc + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + dv*(Icccc+Icncn-Icncc-Icccn)) + dz*(Iccnc-Icccc + dv*(Icccc+Iccnn-Iccnc-Icccn)) + dv*(Icccn-Icccc); } typename cimg::superset::type linear_pix4d(const float fx, const float fy=0, const float fz=0, const float fv=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy), nfz = fz<0?0:(fz>depth-1?depth-1:fz), nfv = fv<0?0:(fv>dim-1?dim-1:fv); const unsigned int x = (unsigned int)nfx, y = (unsigned int)nfy, z = (unsigned int)nfz, v = (unsigned int)nfv; const float dx = nfx-x, dy = nfy-y, dz = nfz-z, dv = nfv-v; const unsigned int nx = dx>0?x+1:x, ny = dy>0?y+1:y, nz = dz>0?z+1:z, nv = dv>0?v+1:v; const ftype Icccc = (ftype)(*this)(x,y,z,v), Inccc = (ftype)(*this)(nx,y,z,v), Icncc = (ftype)(*this)(x,ny,z,v), Inncc = (ftype)(*this)(nx,ny,z,v), Iccnc = (ftype)(*this)(x,y,nz,v), Incnc = (ftype)(*this)(nx,y,nz,v), Icnnc = (ftype)(*this)(x,ny,nz,v), Innnc = (ftype)(*this)(nx,ny,nz,v), Icccn = (ftype)(*this)(x,y,z,nv), Inccn = (ftype)(*this)(nx,y,z,nv), Icncn = (ftype)(*this)(x,ny,z,nv), Inncn = (ftype)(*this)(nx,ny,z,nv), Iccnn = (ftype)(*this)(x,y,nz,nv), Incnn = (ftype)(*this)(nx,y,nz,nv), Icnnn = (ftype)(*this)(x,ny,nz,nv), Innnn = (ftype)(*this)(nx,ny,nz,nv); return Icccc + dx*(Inccc-Icccc + dy*(Icccc+Inncc-Icncc-Inccc + dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc + dv*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) + dv*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) + dz*(Icccc+Incnc-Iccnc-Inccc + dv*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) + dv*(Icccc+Inccn-Inccc-Icccn)) + dy*(Icncc-Icccc + dz*(Icccc+Icnnc-Iccnc-Icncc + dv*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) + dv*(Icccc+Icncn-Icncc-Icccn)) + dz*(Iccnc-Icccc + dv*(Icccc+Iccnn-Iccnc-Icccn)) + dv*(Icccn-Icccc); } //! Read a pixel value using cubic interpolation for the first coordinate \c cx. /** - Same as cubic_pix2d(), except that cubic interpolation and boundary checking is performed only on the first coordinate. \sa operator()(), cubic_pix2d(), linear_pix1d(). **/ typename cimg::superset::type cubic_pix1d(const float fx, const int y, const int z, const int v, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2; const float dx = fx-x; const ftype Ip = (ftype)pix1d(px,y,z,v,out_val), Ic = (ftype)pix1d(x,y,z,v,out_val), In = (ftype)pix1d(nx,y,z,v,out_val), Ia = (ftype)pix1d(ax,y,z,v,out_val), u0 = Ic - Ip, u1 = Ia - In, a = 2*(Ic-In) + u0 + u1, b = 3*(In-Ic) - 2*u0 - u1; return a*dx*dx*dx + b*dx*dx + u0*dx + Ic; } typename cimg::superset::type cubic_pix1d(const float fx, const int y=0, const int z=0, const int v=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx); const int x = (int)nfx; const float dx = nfx-x; const int px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2; const ftype Ip = (ftype)(*this)(px,y,z,v), Ic = (ftype)(*this)(x,y,z,v), In = (ftype)(*this)(nx,y,z,v), Ia = (ftype)(*this)(ax,y,z,v), u0 = Ic - Ip, u1 = Ia - In, a = 2*(Ic-In) + u0 + u1, b = 3*(In-Ic) - 2*u0 - u1; return a*dx*dx*dx + b*dx*dx + u0*dx + Ic; } //! Read a pixel value using bicubic interpolation. /** \param fx X-coordinate of the pixel (float-valued). \param fy Y-coordinate of the pixel (float-valued). \param z Z-coordinate of the pixel. \param v V-coordinate of the pixel. \param out_val Value considered at image borders. - This function allows to read pixel values with boundary checking on the two first coordinates. - If given coordinates are outside the image range, the value of the nearest pixel inside the image is returned (Neumann boundary conditions). - If given coordinates are float-valued, a cubic interpolation is performed in order to compute the returned value. \sa operator()(), cubic_pix1d(), linear_pix2d(). **/ typename cimg::superset::type cubic_pix2d(const float fx, const float fy, const int z, const int v, const T& out_val) const { typedef typename cimg::superset::type ftype; const int x = (int)fx-(fx>=0?0:1), px = x-1, nx = x+1, ax = x+2, y = (int)fy-(fy>=0?0:1), py = y-1, ny = y+1, ay = y+2; const float dx = fx-x, dx2 = dx*dx, dx3 = dx2*dx, dy = fy-y; const ftype Ipp = (ftype)pix2d(px,py,z,v,out_val), Icp = (ftype)pix2d(x,py,z,v,out_val), Inp = (ftype)pix2d(nx,py,z,v,out_val), Iap = (ftype)pix2d(ax,py,z,v,out_val), Ipc = (ftype)pix2d(px,y,z,v,out_val), Icc = (ftype)pix2d(x,y,z,v,out_val), Inc = (ftype)pix2d(nx,y,z,v,out_val), Iac = (ftype)pix2d(ax,y,z,v,out_val), Ipn = (ftype)pix2d(px,ny,z,v,out_val), Icn = (ftype)pix2d(x,ny,z,v,out_val), Inn = (ftype)pix2d(nx,ny,z,v,out_val), Ian = (ftype)pix2d(ax,ny,z,v,out_val), Ipa = (ftype)pix2d(px,ay,z,v,out_val), Ica = (ftype)pix2d(x,ay,z,v,out_val), Ina = (ftype)pix2d(nx,ay,z,v,out_val), Iaa = (ftype)pix2d(ax,ay,z,v,out_val), u0p = Icp - Ipp, u1p = Iap - Inp, ap = 2*(Icp-Inp) + u0p + u1p, bp = 3*(Inp-Icp) - 2*u0p - u1p, u0c = Icc - Ipc, u1c = Iac - Inc, ac = 2*(Icc-Inc) + u0c + u1c, bc = 3*(Inc-Icc) - 2*u0c - u1c, u0n = Icn - Ipn, u1n = Ian - Inn, an = 2*(Icn-Inn) + u0n + u1n, bn = 3*(Inn-Icn) - 2*u0n - u1n, u0a = Ica - Ipa, u1a = Iaa - Ina, aa = 2*(Ica-Ina) + u0a + u1a, ba = 3*(Ina-Ica) - 2*u0a - u1a, valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, valn = an*dx3 + bn*dx2 + u0n*dx + Icn, vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, u0 = valc - valp, u1 = vala - valn, a = 2*(valc-valn) + u0 + u1, b = 3*(valn-valc) - 2*u0 - u1; return a*dy*dy*dy + b*dy*dy + u0*dy + valc; } typename cimg::superset::type cubic_pix2d(const float fx, const float fy, const int z=0, const int v=0) const { typedef typename cimg::superset::type ftype; const float nfx = fx<0?0:(fx>width-1?width-1:fx), nfy = fy<0?0:(fy>height-1?height-1:fy); const int x = (int)nfx, y = (int)nfy; const float dx = nfx-x, dx2 = dx*dx, dx3 = dx2*dx, dy = nfy-y; const int px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=dimx()?dimx()-1:x+2, py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=dimy()?dimy()-1:y+2; const ftype Ipp = (ftype)(*this)(px,py,z,v), Icp = (ftype)(*this)(x,py,z,v), Inp = (ftype)(*this)(nx,py,z,v), Iap = (ftype)(*this)(ax,py,z,v), Ipc = (ftype)(*this)(px,y,z,v), Icc = (ftype)(*this)(x,y,z,v), Inc = (ftype)(*this)(nx,y,z,v), Iac = (ftype)(*this)(ax,y,z,v), Ipn = (ftype)(*this)(px,ny,z,v), Icn = (ftype)(*this)(x,ny,z,v), Inn = (ftype)(*this)(nx,ny,z,v), Ian = (ftype)(*this)(ax,ny,z,v), Ipa = (ftype)(*this)(px,ay,z,v), Ica = (ftype)(*this)(x,ay,z,v), Ina = (ftype)(*this)(nx,ay,z,v), Iaa = (ftype)(*this)(ax,ay,z,v), u0p = Icp - Ipp, u1p = Iap - Inp, ap = 2*(Icp-Inp) + u0p + u1p, bp = 3*(Inp-Icp) - 2*u0p - u1p, u0c = Icc - Ipc, u1c = Iac - Inc, ac = 2*(Icc-Inc) + u0c + u1c, bc = 3*(Inc-Icc) - 2*u0c - u1c, u0n = Icn - Ipn, u1n = Ian - Inn, an = 2*(Icn-Inn) + u0n + u1n, bn = 3*(Inn-Icn) - 2*u0n - u1n, u0a = Ica - Ipa, u1a = Iaa - Ina, aa = 2*(Ica-Ina) + u0a + u1a, ba = 3*(Ina-Ica) - 2*u0a - u1a, valp = ap*dx3 + bp*dx2 + u0p*dx + Icp, valc = ac*dx3 + bc*dx2 + u0c*dx + Icc, valn = an*dx3 + bn*dx2 + u0n*dx + Icn, vala = aa*dx3 + ba*dx2 + u0a*dx + Ica, u0 = valc - valp, u1 = vala - valn, a = 2*(valc-valn) + u0 + u1, b = 3*(valn-valc) - 2*u0 - u1; return a*dy*dy*dy + b*dy*dy + u0*dy + valc; } //! Return a reference to the maximum pixel value of the instance image const T& max() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::max() : Instance image is empty.",pixel_type()); const T *ptrmax = data; T max_value = *ptrmax; cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); return *ptrmax; } //! Return a reference to the maximum pixel value of the instance image T& max() { if (is_empty()) throw CImgInstanceException("CImg<%s>::max() : Instance image is empty.",pixel_type()); T *ptrmax = data; T max_value = *ptrmax; cimg_for(*this,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); return *ptrmax; } //! Return a reference to the minimum pixel value of the instance image const T& min() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::min() : Instance image is empty.",pixel_type()); const T *ptrmin = data; T min_value = *ptrmin; cimg_for(*this,ptr,T) if ((*ptr)::min() : Instance image is empty.",pixel_type()); T *ptrmin = data; T min_value = *ptrmin; cimg_for(*this,ptr,T) if ((*ptr) const T& minmax(t& max_val) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.",pixel_type()); const T *ptrmin = data; T min_value = *ptrmin, max_value = min_value; cimg_for(*this,ptr,T) { const T& val = *ptr; if (valmax_value) max_value = val; } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value and return also the maximum pixel value. template T& minmax(t& max_val) { if (is_empty()) throw CImgInstanceException("CImg<%s>::minmax() : Instance image is empty.",pixel_type()); T *ptrmin = data; T min_value = *ptrmin, max_value = min_value; cimg_for(*this,ptr,T) { const T& val = *ptr; if (valmax_value) max_value = val; } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the maximum pixel value and return also the minimum pixel value. template const T& maxmin(t& min_val) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.",pixel_type()); const T *ptrmax = data; T max_value = *ptrmax, min_value = max_value; cimg_for(*this,ptr,T) { const T& val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val T& maxmin(t& min_val) { if (is_empty()) throw CImgInstanceException("CImg<%s>::maxmin() : Instance image is empty.",pixel_type()); T *ptrmax = data; T max_value = *ptrmax, min_value = max_value; cimg_for(*this,ptr,T) { const T& val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val::mean() : Instance image is empty.",pixel_type()); double val = 0; cimg_for(*this,ptr,T) val+=(double)*ptr; return val/size(); } //! Return the variance and the mean of the image template double variancemean(const unsigned int variance_method, t& mean) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::variance() : Instance image is empty.",pixel_type()); double variance = 0, average = 0; const unsigned int siz = size(); switch (variance_method) { case 3: // Least trimmed of Squares { CImg buf(*this); const unsigned int siz2 = siz>>1; { cimg_for(buf,ptrs,double) { const double val = *ptrs; (*ptrs)*=val; average+=val; } } buf.sort(); double a = 0; const double *ptrs = buf.ptr(); for (unsigned int j=0; j buf(*this); buf.sort(); const unsigned int siz2 = siz>>1; const double med_i = buf[siz2]; cimg_for(buf,ptrs,double) { const double val = *ptrs; *ptrs = cimg::abs(val-med_i); average+=val; } buf.sort(); const double sig = 1.4828*buf[siz2]; variance = sig*sig; } break; case 1: // Least mean square (robust definition) { double S = 0, S2 = 0; cimg_for(*this,ptr,T) { const double val = (double)*ptr; S+=val; S2+=val*val; } variance = siz>1?(S2 - S*S/siz)/(siz-1):0; average = S; } break; default: // Least mean square (standard definition) { double S = 0, S2 = 0; cimg_for(*this,ptr,T) { const double val = (double)*ptr; S+=val; S2+=val*val; } variance = (S2 - S*S/siz)/siz; average = S; } break; } mean = (t)(average/siz); return variance; } //! Return the variance and the mean of the image double variance(const unsigned int variance_method=0) const { double foo; return variancemean(variance_method,foo); } //! Compute the MSE (Mean-Squared Error) between two images. template double MSE(const CImg& img) const { if (img.size()!=size()) throw CImgArgumentException("CImg<%s>::MSE() : Instance image (%u,%u,%u,%u) and given image (%u,%u,%u,%u) have different dimensions.", pixel_type(),width,height,depth,dim,img.width,img.height,img.depth,img.dim); double vMSE = 0; const t* ptr2 = img.end(); cimg_for(*this,ptr1,T) { const double diff = (double)*ptr1 - (double)*(--ptr2); vMSE += diff*diff; } vMSE/=img.size(); return vMSE; } //! Compute the PSNR between two images. template double PSNR(const CImg& img, const double valmax=255.0) const { const double vMSE = std::sqrt(MSE(img)); return (vMSE!=0)?(20*std::log10(valmax/vMSE)):(cimg::type::max()); } //! Return the trace of the current matrix. double trace() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::trace() : Instance matrix (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); double res = 0; cimg_forX(*this,k) res+=(*this)(k,k); return res; } //! Return the median of the image T median() const { const unsigned int s = size(); const T res = kth_smallest(s>>1); return (s%2)?res:((res+kth_smallest((s>>1)-1))/2); } //! Return the dot product of the current vector/matrix with the vector/matrix \p img. double dot(const CImg& img) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::dot() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (!img) throw CImgArgumentException("CImg<%s>::trace() : Specified argument (%u,%u,%u,%u,%p) is empty.", pixel_type(),img.width,img.height,img.depth,img.dim,img.data); const unsigned long nb = cimg::min(size(),img.size()); double res = 0; for (unsigned long off=0; off::det() : Instance matrix (%u,%u,%u,%u,%p) is not square or is empty.", pixel_type(),width,height,depth,dim,data); switch (width) { case 1: return (*this)(0,0); case 2: return (*this)(0,0)*(*this)(1,1)-(*this)(0,1)*(*this)(1,0); case 3: { const double a = data[0], d = data[1], g = data[2], b = data[3], e = data[4], h = data[5], c = data[6], f = data[7], i = data[8]; return i*a*e-a*h*f-i*b*d+b*g*f+c*d*h-c*g*e; } default: { typedef typename cimg::superset::type ftype; CImg lu(*this); CImg indx; bool d; lu._LU(indx,d); double res = d?1.0:-1.0; cimg_forX(lu,i) res*=lu(i,i); return res; } } return 0; } //! Return the norm of the current vector/matrix. \p ntype = norm type (0=L2, 1=L1, -1=Linf). double norm(const int norm_type=2) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::norm() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); double res = 0; switch (norm_type) { case -1: { cimg_foroff(*this,off) { const double tmp = cimg::abs((double)data[off]); if (tmp>res) res = tmp; } return res; } break; case 1 : { cimg_foroff(*this,off) res+=cimg::abs((double)data[off]); return res; } break; default: { return std::sqrt(dot(*this)); } } return 0; } //! Return the sum of all the pixel values in an image. double sum() const { if (is_empty()) throw CImgInstanceException("CImg<%s>::sum() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); double res = 0; cimg_for(*this,ptr,T) res+=*ptr; return res; } //! Return the kth smallest element of the image // (Adapted from the numerical recipies for CImg) const T kth_smallest(const unsigned int k) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::kth_smallest() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); CImg arr(*this); unsigned long l = 0, ir = size()-1; for (;;) { if (ir<=l+1) { if (ir==l+1 && arr[ir]>1; cimg::swap(arr[mid],arr[l+1]); if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]); if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]); if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]); unsigned long i = l+1, j = ir; const T pivot = arr[l+1]; for (;;) { do ++i; while (arr[i]pivot); if (j=k) ir=j-1; if (j<=k) l=i; } } return 0; } //! Display informations about the image on the standard error output. /** \param title Name for the considered image (optional). \param print_flag Level of informations to be printed. - The possible values for \c print_flag are : - -1 : print nothing - 0 : print only informations about image size and pixel buffer. - 1 : print also statistics on the image pixels. - 2 : print also the content of the pixel buffer, in a matlab-style. \par example: \code CImg img("foo.jpg"); // Load image from a JPEG file. img.print("Image : foo.jpg",1); // Print image informations and statistics. \endcode **/ const CImg& print(const char *title=0, const int print_flag=1) const { typedef typename cimg::last::type cdouble; static CImg st; if (print_flag>=0) { std::fprintf(stderr,"%-8s(this=%p): { size=(%u,%u,%u,%u), data=(%s*)%p (%s)", title?title:"CImg",(void*)this, width,height,depth,dim,pixel_type(),(void*)data, is_shared?"shared":"not shared"); if (is_empty()) { std::fprintf(stderr,", [Undefined pixel data] }\n"); return *this; } if (print_flag>=1) { st = get_stats(); int xm, ym, zm, vm, xM, yM, zM, vM; contains(data[(int)st(4)],xm,ym,zm,vm); contains(data[(int)st(5)],xM,yM,zM,vM); std::fprintf(stderr,", min=%g, mean=%g [std=%g], max=%g, pmin=(%d,%d,%d,%d), pmax=(%d,%d,%d,%d)", st[0],st[2],std::sqrt(st[3]),st[1],xm,ym,zm,vm,xM,yM,zM,vM); } if (print_flag>=2 || size()<=16) { std::fprintf(stderr," }\n%s = [ ",title?title:"data"); cimg_forXYZV(*this,x,y,z,k) { std::fprintf(stderr,cimg::type::format(),cimg::type::format((*this)(x,y,z,k))); std::fprintf(stderr,"%s",((x+1)*(y+1)*(z+1)*(k+1)==(int)size()?" ]\n":(((x+1)%width==0)?" ; ":" "))); } } else std::fprintf(stderr," }\n"); } return *this; } //! Display informations about the image on the standard output. const CImg& print(const int print_flag) const { return print(0,print_flag); } //@} //------------------------------------------ // //! \name Arithmetic and Boolean Operators //@{ //------------------------------------------ //! Assignment operator. /** This operator assigns a copy of the input image \p img to the current instance image. \param img The input image to copy. \remark - This operator is strictly equivalent to the function assign(const CImg< t >&) and has exactly the same properties. \see assign(const CImg< t >&). **/ template CImg& operator=(const CImg& img) { return assign(img); } CImg& operator=(const CImg& img) { return assign(img); } //! Assign values of a C-array to the instance image. /** \param buf Pointer to a C-style array having a size of (at least) this->size(). - Replace pixel values by the content of the array \c buf. - Warning : the value types in the array and in the image must be the same. \par example: \code float tab[4*4] = { 1,2,3,4, 5,6,7,8, 9,10,11,12, 13,14,15,16 }; // Define a 4x4 matrix in C-style. CImg matrice(4,4); // Define a 4x4 greyscale image. matrice = tab; // Fill the image by the values in tab. \endcode **/ CImg& operator=(const T *buf) { if (buf) std::memcpy(data,buf,size()*sizeof(T)); else assign(); return *this; } //! Assign a value to each image pixel of the instance image. CImg& operator=(const T& val) { return fill(val); } //! Operator+ /** \remark - This operator can be used to get a non-shared copy of an image. **/ CImg operator+() const { return CImg(*this,false); } //! Operator+=; #ifdef cimg_use_visualcpp6 CImg& operator+=(const T& val) { #else template CImg& operator+=(const t& val) { #endif cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)+val); return *this; } //! Operator+= template CImg& operator+=(const CImg& img) { const unsigned int smin = cimg::min(size(),img.size()); t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; --ptrd, (*ptrd)=(T)((*ptrd)+(*(--ptrs)))); return *this; } //! Operator++ (prefix) CImg& operator++() { cimg_for(*this,ptr,T) ++(*ptr); return *this; } //! Operator++ (postfix) CImg operator++(int) { CImg copy(*this,false); ++*this; return copy; } //! Operator-. CImg operator-() const { return CImg(width,height,depth,dim,0)-=*this; } //! Operator-=. #ifdef cimg_use_visualcpp6 CImg& operator-=(const T& val) { #else template CImg& operator-=(const t& val) { #endif cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)-val); return *this; } //! Operator-=. template CImg& operator-=(const CImg& img) { const unsigned int smin = cimg::min(size(),img.size()); t *ptrs = img.data+smin; for (T *ptrd = data+smin; ptrd>data; --ptrd, (*ptrd) = (T)((*ptrd)-(*(--ptrs)))); return *this; } //! Operator-- (prefix). CImg& operator--() { cimg_for(*this,ptr,T) *ptr = *ptr-(T)1; return *this; } //! Operator-- (postfix). CImg operator--(int) { CImg copy(*this,false); --*this; return copy; } //! Operator*=. #ifdef cimg_use_visualcpp6 CImg& operator*=(const double val) { #else template CImg& operator*=(const t& val) { #endif cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)*val); return *this; } //! Operator*=. template CImg& operator*=(const CImg& img) { return ((*this)*img).assign_to(*this); } //! Operator/=. #ifdef cimg_use_visualcpp6 CImg& operator/=(const double val) { #else template CImg& operator/=(const t& val) { #endif cimg_for(*this,ptr,T) (*ptr) = (T)((*ptr)/val); return *this; } //! Operator/=. template CImg& operator/=(const CImg& img) { return assign(*this*img.get_inverse()); } //! Modulo. template CImg::type> operator%(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false)%=img; } //! Modulo. CImg operator%(const T& val) const { return (+*this)%=val; } //! In-place modulo. CImg& operator%=(const T& val) { cimg_for(*this,ptr,T) (*ptr) = (T)cimg::mod(*ptr,val); return *this; } //! In-place modulo. template CImg& operator%=(const CImg& img) { typedef typename cimg::superset::type btype; const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)cimg::mod((btype)val,(btype)*(--ptrs)); } return *this; } //! Bitwise AND. template CImg::type> operator&(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false)&=img; } //! Bitwise AND. CImg operator&(const T& val) const { return (+*this)&=val; } //! In-place bitwise AND. template CImg& operator&=(const CImg& img) { const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)((unsigned long)val & (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise AND. CImg& operator&=(const T& val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr & (unsigned long)val); return *this; } //! Bitwise OR. template CImg::type> operator|(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false)|=img; } //! Bitwise OR. CImg operator|(const T& val) const { return (+*this)|=val; } //! In-place bitwise OR. template CImg& operator|=(const CImg& img) { const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data + smin; ptrd>data; ) { T& val = *(--ptrd); val = (T)((unsigned long)val | (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise OR. CImg& operator|=(const T& val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr | (unsigned long)val); return *this; } //! Bitwise XOR. template CImg::type> operator^(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false)^=img; } //! Bitwise XOR. CImg operator^(const T& val) const { return (+*this)^=val; } //! In-place bitwise XOR. template CImg& operator^=(const CImg& img) { const unsigned int smin = cimg::min(size(),img.size()); const t *ptrs = img.data + smin; for (T *ptrd = data+smin; ptrd>data; ) { T& val = *(--ptrd); val =(T)((unsigned long)val ^ (unsigned long)*(--ptrs)); } return *this; } //! In-place bitwise XOR. CImg& operator^=(const T& val) { cimg_for(*this,ptr,T) *ptr = (T)((unsigned long)*ptr ^ (unsigned long)val); return *this; } //! Bitwise NOT. CImg operator~() const { CImg res(width,height,depth,dim); const T *ptrs = end(); cimg_for(res,ptrd,T) *ptrd = (T)~(unsigned long)*(--ptrs); return res; } //! Bitwise shift CImg& operator<<=(const int n) { cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)<>=(const int n) { cimg_for(*this,ptr,T) *ptr = (T)(((long)*ptr)>>n); return *this; } //! Bitwise shift CImg operator>>(const int n) const { return (+*this)>>=n; } //! Boolean equality. template bool operator==(const CImg& img) const { const unsigned int siz = size(); bool vequal = true; if (siz!=img.size()) return false; t *ptrs=img.data+siz; for (T *ptrd=data+siz; vequal && ptrd>data; vequal=vequal&&((*(--ptrd))==(*(--ptrs)))); return vequal; } //! Boolean difference. template bool operator!=(const CImg& img) const { return !((*this)==img); } //! Return a list of two images { *this, img } template CImgList::type> operator<<(const CImg& img) const { typedef typename cimg::superset::type restype; return CImgList(*this,img); } //! Return a copy of \p list, where image *this has been inserted at first position. template CImgList::type> operator<<(const CImgList& list) const { typedef typename cimg::superset::type restype; return CImgList(list).insert(*this,0); } //! Return a list of two images { *this, img } template CImgList::type> operator>>(const CImg& img) const { return (*this)< CImgList& operator>>(const CImgList& list) const { return list.insert(*this,0); } //! Display an image into a CImgDisplay const CImg& operator>>(CImgDisplay& disp) const { return display(disp); } //@} //--------------------------------------- // //! \name Usual Mathematics Functions //@{ //--------------------------------------- //! Apply a R->R function on all pixel values. template CImg get_apply(t& func) const { return (+*this).apply(func); } //! In-place version of the previous function. template CImg& apply(t& func) { cimg_for(*this,ptr,T) *ptr = func(*ptr); return *this; } //! Pointwise multiplication between two images. template CImg::type> get_mul(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false).mul(img); } //! In-place version of the previous function. template CImg& mul(const CImg& img) { t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_div(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false).div(img); } //! In-place version of the previous function. template CImg& div(const CImg& img) { t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_max(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false).max(img); } //! In-place version of the previous function. template CImg& max(const CImg& img) { t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd CImg::type> get_min(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false).min(img); } //! In-place version of the previous function. template CImg& min(const CImg& img) { t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd::type> get_stats() const { typedef typename cimg::last::type restype; return CImg(*this).stats(); } //! In-place version of the previous function. CImg& stats() { if (is_empty()) throw CImgInstanceException("CImg<%s>::stats() : Instance image is empty.",pixel_type()); const unsigned long siz = size(); const T *pm = data, *pM = pm; double S = 0, S2 = 0; T m = *pm, M = m; cimg_for(*this,ptr,T) { const T& val = *ptr; const double fval = (double)val; if (valM) { M = val; pM = ptr; } S+=fval; S2+=fval*fval; } return assign(1,6).fill((T)m,(T)M,(T)(S/siz),(T)((S2-S*S/siz)/siz),(T)(pm-data),(T)(pM-data)); } //! Compute the square of each pixel value. CImg::type> get_sqr() const { typedef typename cimg::superset::type restype; return CImg(*this,false).sqr(); } //! In-place version of the previous function. CImg& sqr() { cimg_for(*this,ptr,T) { const T val = *ptr; *ptr = (T)(val*val); }; return *this; } //! Compute the square root of each pixel value. CImg::type> get_sqrt() const { typedef typename cimg::superset::type restype; return CImg(*this,false).sqrt(); } //! In-place version of the previous function. CImg& sqrt() { cimg_for(*this,ptr,T) (*ptr) = (T)std::sqrt((double)(*ptr)); return *this; } //! Compute the exponential of each pixel value. CImg::type> get_exp() const { typedef typename cimg::superset::type restype; return CImg(*this,false).exp(); } //! In-place version of the previous function. CImg& exp() { cimg_for(*this,ptr,T) (*ptr) = (T)std::exp((double)(*ptr)); return *this; } //! Compute the log of each each pixel value. CImg::type> get_log() const { typedef typename cimg::superset::type restype; return CImg(*this,false).log(); } //! In-place version of the previous function. CImg& log() { cimg_for(*this,ptr,T) (*ptr) = (T)std::log((double)(*ptr)); return *this; } //! Compute the log10 of each each pixel value. CImg::type> get_log10() const { typedef typename cimg::superset::type restype; return CImg(*this,false).log10(); } //! In-place version of the previous function. CImg& log10() { cimg_for(*this,ptr,T) (*ptr) = (T)std::log10((double)(*ptr)); return *this; } //! Compute the power by p of each pixel value. CImg::type> get_pow(const double p) const { typedef typename cimg::superset::type restype; return CImg(*this,false).pow(p); } //! In-place version of the previous function. CImg& pow(const double p) { if (p==0) return fill(1); if (p==0.5) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr = (T)std::sqrt((double)val); } return *this; } if (p==1) return *this; if (p==2) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr = val*val; } return *this; } if (p==3) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr = val*val*val; } return *this; } if (p==4) { cimg_for(*this,ptr,T) { const T& val = *ptr; *ptr = val*val*val*val; } return *this; } cimg_for(*this,ptr,T) (*ptr) = (T)std::pow((double)(*ptr),p); return *this; } //! Compute the power of each pixel value. template CImg::type> get_pow(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this,false).pow(img); } //! In-place version of the previous function. template CImg& pow(const CImg& img) { t *ptrs = img.data; T *ptrf = data + cimg::min(size(),img.size()); for (T* ptrd = data; ptrd::type> get_abs() const { typedef typename cimg::superset::type restype; return CImg(*this,false).abs(); } //! In-place version of the previous function. CImg& abs() { cimg_for(*this,ptr,T) (*ptr) = cimg::abs(*ptr); return *this; } //! Compute the cosinus of each pixel value. CImg::type> get_cos() const { typedef typename cimg::superset::type restype; return CImg(*this,false).cos(); } //! In-place version of the previous function. CImg& cos() { cimg_for(*this,ptr,T) (*ptr) = (T)std::cos((double)(*ptr)); return *this; } //! Compute the sinus of each pixel value. CImg::type> get_sin() const { typedef typename cimg::superset::type restype; return CImg(*this,false).sin(); } //! In-place version of the previous function. CImg& sin() { cimg_for(*this,ptr,T) (*ptr) = (T)std::sin((double)(*ptr)); return *this; } //! Compute the tangent of each pixel. CImg::type> get_tan() const { typedef typename cimg::superset::type restype; return CImg(*this,false).tan(); } //! In-place version of the previous function. CImg& tan() { cimg_for(*this,ptr,T) (*ptr) = (T)std::tan((double)(*ptr)); return *this; } //! Compute the arc-cosine of each pixel value. CImg::type> get_acos() const { typedef typename cimg::superset::type restype; return CImg(*this,false).acos(); } //! In-place version of the previous function. CImg& acos() { cimg_for(*this,ptr,T) (*ptr) = (T)std::acos((double)(*ptr)); return *this; } //! Compute the arc-sinus of each pixel value. CImg::type> get_asin() const { typedef typename cimg::superset::type restype; return CImg(*this,false).asin(); } //! In-place version of the previous function. CImg& asin() { cimg_for(*this,ptr,T) (*ptr) = (T)std::asin((double)(*ptr)); return *this; } //! Compute the arc-tangent of each pixel. CImg::type> get_atan() const { typedef typename cimg::superset::type restype; return CImg(*this,false).atan(); } //! In-place version of the previous function. CImg& atan() { cimg_for(*this,ptr,T) (*ptr) = (T)std::atan((double)(*ptr)); return *this; } //! Compute image with rounded pixel values. /** \param x Rounding precision. \param round_type Roundin type, can be 0 (nearest), 1 (forward), 2 (backward). **/ CImg get_round(const float x, const unsigned int round_type=0) const { return (+*this).round(x,round_type); } //! In-place version of the previous function. CImg& round(const float x, const unsigned int round_type=0) { cimg_for(*this,ptr,T) (*ptr) = (T)cimg::round(*ptr,x,round_type); return *this; } //! Fill image with random values between specified range. CImg get_rand(const T& val_min, const T& val_max) const { return (+*this).rand(val_min,val_max); } //! In-place version of the previous function. CImg& rand(const T& val_min, const T& val_max) { const float delta = (float)val_max - (float)val_min; cimg_for(*this,ptr,T) *ptr = (T)(val_min + cimg::rand()*delta); return *this; } //@} //----------------------------------- // //! \name Usual Image Transformations //@{ //----------------------------------- //! Fill an image by a value \p val. /** \param val = fill value \note All pixel values of the instance image will be initialized by \p val. \see operator=(). **/ CImg get_fill(const T& val) const { return CImg(width,height,depth,dim).fill(val); } //! In-place version of the previous function. CImg& fill(const T& val) { if (!is_empty()) { if (val && sizeof(T)!=1) cimg_for(*this,ptr,T) *ptr = val; else std::memset(data,(int)val,size()*sizeof(T)); } return *this; } //! Fill sequentially all pixel values with values \a val0 and \a val1 respectively. CImg get_fill(const T& val0, const T& val1) const { return CImg(width,height,depth,dim).fill(val0,val1); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1) { if (!is_empty()) { T *ptr, *ptr_end = end()-1; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2) { if (!is_empty()) { T *ptr, *ptr_end = end()-2; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3) { if (!is_empty()) { T *ptr, *ptr_end = end()-3; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) { if (!is_empty()) { T *ptr, *ptr_end = end()-4; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) { if (!is_empty()) { T *ptr, *ptr_end = end()-5; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6) { if (!is_empty()) { T *ptr, *ptr_end = end()-6; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7) { if (!is_empty()) { T *ptr, *ptr_end = end()-7; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8) { if (!is_empty()) { T *ptr, *ptr_end = end()-8; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9) { if (!is_empty()) { T *ptr, *ptr_end = end()-9; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) { if (!is_empty()) { T *ptr, *ptr_end = end()-10; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) { if (!is_empty()) { T *ptr, *ptr_end = end()-11; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12) { if (!is_empty()) { T *ptr, *ptr_end = end()-12; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13) { if (!is_empty()) { T *ptr, *ptr_end = end()-13; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13, const T& val14) { if (!is_empty()) { T *ptr, *ptr_end = end()-14; for (ptr=data; ptr(width,height,depth,dim).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,val13,val14,val15); } //! In-place version of the previous function. CImg& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5, const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11, const T& val12, const T& val13, const T& val14, const T& val15) { if (!is_empty()) { T *ptr, *ptr_end = end()-15; for (ptr=data; ptr CImg get_fill(const int& val0, ...) const { CImg res(*this,false); va_list ap; va_start(ap,val0); res._fill(val0,ap); va_end(ap); return res; } //! In-place version of the previous function. template CImg& fill(const int& val0, ...) { va_list ap; va_start(ap,val0); _fill(val0,ap); va_end(ap); return *this; } //! Fill sequentially pixel values. template CImg get_fill(const double& val0, ...) const { CImg res(*this,false); va_list ap; va_start(ap,val0); res._fill(val0,ap); va_end(ap); return res; } //! In-place version of the previous function. template CImg& fill(const double& val0, ...) { va_list ap; va_start(ap,val0); _fill(val0,ap); va_end(ap); return *this; } template CImg& _fill(const t& val0, va_list &ap) { if (N>0 && !is_empty()) { CImg vals(N); T *ptrs = vals.data; *(ptrs++) = (T)val0; for (int i=1; i0; --i) *(ptr++) = *(ptrs++); } return *this; } //! Fill image values along the V-axis at the specified pixel position (x,y,z) (int version) CImg& fillV(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) { #define _cimg_fillv(x,y,z,a0,t) if (x::type ftype; T m, M = maxmin(m); const ftype fm = (ftype)m, fM = (ftype)M; if (m==M) return fill(0); if (m!=a || M!=b) cimg_for(*this,ptr,T) *ptr = (T)((*ptr-fm)/(fM-fm)*(b-a)+a); } return *this; } //! Cut pixel values between \a a and \a b. /** \param a = minimum pixel value after cut. \param b = maximum pixel value after cut. **/ CImg get_cut(const T& a, const T& b) const { return (+*this).cut(a,b); } //! In-place version of the previous function. CImg& cut(const T& a, const T& b) { if (!is_empty()) cimg_for(*this,ptr,T) *ptr = (*ptrb)?b:*ptr); return *this; } //! Quantize pixel values into \n levels. /** \param n = number of quantification levels \param keep_range = keep same value range. **/ CImg get_quantize(const unsigned int n=256, const bool keep_range=true) const { return (+*this).quantize(n,keep_range); } //! In-place version of the previous function. CImg& quantize(const unsigned int n=256, const bool keep_range=true) { if (!is_empty()) { if (!n) throw CImgArgumentException("CImg<%s>::quantize() : Cannot quantize image to 0 values.", pixel_type()); typedef typename cimg::superset::type ftype; ftype m, M = (ftype)maxmin(m), range = M - m; if (range>0) { if (keep_range) cimg_for(*this,ptr,T) { const unsigned int val = (unsigned int)((*ptr-m)*n/range); *ptr = (T)(m + cimg::min(val,n-1)*range/n); } else cimg_for(*this,ptr,T) { const unsigned int val = (unsigned int)((*ptr-m)*n/range); *ptr = (T)cimg::min(val,n-1); } } } return *this; } //! Threshold the image. /** \param thres = threshold **/ CImg get_threshold(const T& thres) const { return (+*this).threshold(thres); } //! In-place version of the previous function. CImg& threshold(const T& thres) { if (!is_empty()) cimg_for(*this,ptr,T) *ptr = *ptr<=thres?(T)0:(T)1; return *this; } //! Return a rotated image. /** \param angle = rotation angle (in degrees). \param cond = rotation type. can be : - 0 = zero-value at borders - 1 = repeat image at borders - 2 = zero-value at borders and linear interpolation \note Returned image will probably have a different size than the instance image *this. **/ CImg get_rotate(const float angle, const unsigned int cond=3) const { if (is_empty()) return CImg(); CImg dest; const float nangle = cimg::mod(angle,360.0f), rad = (float)((nangle*cimg::PI)/180.0), ca=(float)std::cos(rad), sa=(float)std::sin(rad); if (cond!=1 && cimg::mod(nangle,90.0f)==0) // optimized version for orthogonal angles { const int wm1 = dimx()-1, hm1 = dimy()-1; const int iangle = (int)nangle/90; switch (iangle) { case 1: { dest.assign(height,width,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(y,hm1-x,z,v); } break; case 2: { dest.assign(width,height,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-x,hm1-y,z,v); } break; case 3: { dest.assign(height,width,depth,dim); cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = (*this)(wm1-y,x,z,v); } break; default: return *this; } } else // generic version { const float ux = (float)(cimg::abs(width*ca)), uy = (float)(cimg::abs(width*sa)), vx = (float)(cimg::abs(height*sa)), vy = (float)(cimg::abs(height*ca)), w2 = 0.5f*width, h2 = 0.5f*height, dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy); dest.assign((int)(ux+vx), (int)(uy+vy),depth,dim); switch (cond) { case 0: { cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = pix2d((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,v,0); } break; case 1: { cimg_forXY(dest,x,y) cimg_forZV(*this,z,v) dest(x,y,z,v) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),dimx()), cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),dimy()),z,v); } break; case 2: { cimg_forXY(dest,x,y) { const float X = w2 + (x-dw2)*ca + (y-dh2)*sa, Y = h2 - (x-dw2)*sa + (y-dh2)*ca; cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)linear_pix2d(X,Y,z,v,0); } } break; default: { cimg_forXY(dest,x,y) { const float X = w2 + (x-dw2)*ca + (y-dh2)*sa, Y = h2 - (x-dw2)*sa + (y-dh2)*ca; cimg_forZV(*this,z,v) dest(x,y,z,v) = (T)cubic_pix2d(X,Y,z,v,0); } } break; } } return dest; } //! In-place version of the previous function. CImg& rotate(const float angle, const unsigned int cond=3) { return get_rotate(angle,cond).assign_to(*this); } //! Return a rotated image around the point (\c cx,\c cy). /** \param angle = rotation angle (in degrees). \param cx = X-coordinate of the rotation center. \param cy = Y-coordinate of the rotation center. \param zoom = zoom. \param cond = rotation type. can be : - 0 = zero-value at borders - 1 = repeat image at borders - 2 = zero-value at borders and linear interpolation \see rotate() **/ CImg get_rotate(const float angle, const float cx, const float cy, const float zoom=1, const unsigned int cond=3) const { if (is_empty()) return CImg(); CImg dest(width,height,depth,dim); const float nangle = cimg::mod(angle,360.0f), rad = (float)((nangle*cimg::PI)/180.0), ca=(float)std::cos(rad)/zoom, sa=(float)std::sin(rad)/zoom; if (cond!=1 && zoom==1 && cimg::mod(nangle,90.0f)==0) // optimized version for orthogonal angles { const int iangle = (int)nangle/90; switch (iangle) { case 1: { dest.fill(0); const unsigned int xmin = cimg::max(0,(dimx()-dimy())/2), xmax = cimg::min(width,xmin+height), ymin = cimg::max(0,(dimy()-dimx())/2), ymax = cimg::min(height,ymin+width), xoff = xmin + cimg::min(0,(dimx()-dimy())/2), yoff = ymin + cimg::min(0,(dimy()-dimx())/2); cimg_forZV(dest,z,v) for (unsigned int y=ymin; y(); const unsigned int tdx = pdx<0?-pdx*width/100:pdx, tdy = pdy<0?-pdy*height/100:pdy, tdz = pdz<0?-pdz*depth/100:pdz, tdv = pdv<0?-pdv*dim/100:pdv, dx = tdx?tdx:1, dy = tdy?tdy:1, dz = tdz?tdz:1, dv = tdv?tdv:1; if (width==dx && height==dy && depth==dz && dim==dv) return *this; if (is_empty()) return CImg(dx,dy,dz,dv,0); CImg res; switch (interp) { case -1: // Raw resizing std::memcpy(res.assign(dx,dy,dz,dv,0).data,data,sizeof(T)*cimg::min(size(),(long unsigned int)dx*dy*dz*dv)); break; case 0: // Zero filling res.assign(dx,dy,dz,dv,0).draw_image(*this,0,0,0,0); break; case 1: // Nearest-neighbor interpolation { res.assign(dx,dy,dz,dv); unsigned int *const offx = new unsigned int[dx], *const offy = new unsigned int[dy+1], *const offz = new unsigned int[dz+1], *const offv = new unsigned int[dv+1], *poffx, *poffy, *poffz, *poffv, curr, old; const unsigned int wh = width*height, whd = width*height*depth, rwh = dx*dy, rwhd = dx*dy*dz; poffx = offx; curr = 0; { cimg_forX(res,x) { old=curr; curr=(x+1)*width/dx; *(poffx++) = (unsigned int)curr-(unsigned int)old; } } poffy = offy; curr = 0; { cimg_forY(res,y) { old=curr; curr=(y+1)*height/dy; *(poffy++) = width*((unsigned int)curr-(unsigned int)old); } } *poffy=0; poffz = offz; curr = 0; { cimg_forZ(res,z) { old=curr; curr=(z+1)*depth/dz; *(poffz++) = wh*((unsigned int)curr-(unsigned int)old); } } *poffz=0; poffv = offv; curr = 0; { cimg_forV(res,k) { old=curr; curr=(k+1)*dim/dv; *(poffv++) = whd*((unsigned int)curr-(unsigned int)old); } } *poffv=0; T *ptrd = res.data; const T* ptrv = data; poffv = offv; for (unsigned int k=0; kwidth )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; unsigned int *const off = new unsigned int[dimmax], *poff; float *const foff = new float[dimmax], *pfoff, old, curr; CImg resx, resy, resz, resv; T *ptrd; if (dx!=width) { if (width==1) resx = get_resize(dx,height,depth,dim,1,0); else { resx.assign(dx,height,depth,dim); curr = old = 0; poff = off; pfoff = foff; cimg_forX(resx,x) { *(pfoff++) = curr-(unsigned int)curr; old = curr; curr+=sx; *(poff++) = (unsigned int)curr-(unsigned int)old; } ptrd = resx.data; const T *ptrs0 = data; cimg_forYZV(resx,y,z,k) { poff = off; pfoff = foff; const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (width-1); cimg_forX(resx,x) { const float alpha = *(pfoff++); const T val1 = *ptrs, val2 = ptrs::type ftype; const float sx = (border_condition<0 && dx>width )?(dx>1?(width-1.0f)/(dx-1) :0):(float)width/dx, sy = (border_condition<0 && dy>height)?(dy>1?(height-1.0f)/(dy-1):0):(float)height/dy, sz = (border_condition<0 && dz>depth )?(dz>1?(depth-1.0f)/(dz-1) :0):(float)depth/dz, sv = (border_condition<0 && dv>dim )?(dv>1?(dim-1.0f)/(dv-1) :0):(float)dim/dv; ftype valmin = 0, valmax = (ftype)maxmin(valmin); res.assign(dx,dy,dz,dv); T *ptrd = res.ptr(); float cx, cy, cz, ck = 0; cimg_forV(res,k) { cz = 0; cimg_forZ(res,z) { cy = 0; cimg_forY(res,y) { cx = 0; cimg_forX(res,x) { const ftype val = (ftype)(border_condition?cubic_pix2d(cx,cy,(int)cz,(int)ck):cubic_pix2d(cx,cy,(int)cz,(int)ck,0)); *(ptrd++) = (T)(valvalmax?valmax:val)); cx+=sx; } cy+=sy; } cz+=sz; } ck+=sv; } } break; case 6: // Moving average { typedef typename cimg::superset::type ftype; bool instance_first = true; if (dx!=width) { CImg tmp(dx,height,depth,dim,0); for (unsigned int a=width*dx, b=width, c=dx, s=0, t=0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)+=(ftype)(*this)(s,y,z,v)*d; if (!b) { cimg_forYZV(tmp,y,z,v) tmp(t,y,z,v)/=width; ++t; b = width; } if (!c) { ++s; c = dx; } } tmp.assign_to(res); instance_first = false; } if (dy!=height) { CImg tmp(dx,dy,depth,dim,0); for (unsigned int a=height*dy, b=height, c=dy, s=0, t=0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(ftype)(*this)(x,s,z,v)*d; else cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)+=(ftype)res(x,s,z,v)*d; if (!b) { cimg_forXZV(tmp,x,z,v) tmp(x,t,z,v)/=height; ++t; b = height; } if (!c) { ++s; c = dy; } } tmp.assign_to(res); instance_first = false; } if (dz!=depth) { CImg tmp(dx,dy,dz,dim,0); for (unsigned int a=depth*dz, b=depth, c=dz, s=0, t=0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(ftype)(*this)(x,y,s,v)*d; else cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)+=(ftype)res(x,y,s,v)*d; if (!b) { cimg_forXYV(tmp,x,y,v) tmp(x,y,t,v)/=depth; ++t; b = depth; } if (!c) { ++s; c = dz; } } tmp.assign_to(res); instance_first = false; } if (dv!=dim) { CImg tmp(dx,dy,dz,dv,0); for (unsigned int a=dim*dv, b=dim, c=dv, s=0, t=0; a; ) { const unsigned int d = cimg::min(b,c); a-=d; b-=d; c-=d; if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(ftype)(*this)(x,y,z,s)*d; else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(ftype)res(x,y,z,s)*d; if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=dim; ++t; b = dim; } if (!c) { ++s; c = dv; } } tmp.assign_to(res); instance_first = false; } } break; default: // Invalid interpolation method throw CImgArgumentException("CImg<%s>::resize() : Invalid interpolation method : %d specified.", pixel_type(),interp); } return res; } //! In-place version of the previous function. CImg& resize(const int pdx=-100, const int pdy=-100, const int pdz=-100, const int pdv=-100, const int interp=1, const int border_condition=-1) { if (!pdx || !pdy || !pdz || !pdv) return assign(); const unsigned int dx = pdx<0?-pdx*width/100:pdx, dy = pdy<0?-pdy*height/100:pdy, dz = pdz<0?-pdz*depth/100:pdz, dv = pdv<0?-pdv*dim/100:pdv; if (width==dx && height==dy && depth==dz && dim==dv) return *this; if (interp==-1 && dx*dy*dz*dv==size()) { width = dx; height = dy; depth = dz; dim = dv; return *this; } return get_resize(dx,dy,dz,dv,interp,border_condition).assign_to(*this); } //! Return a resized image. /** \param src Image giving the geometry of the resize. \param interp Interpolation method : - 1 = raw memory - 0 = no interpolation : additional space is filled with 0. - 1 = bloc interpolation (nearest point). - 2 = mosaic : image is repeated if necessary. - 3 = linear interpolation. - 4 = grid interpolation. - 5 = bi-cubic interpolation. \param border_condition Border condition type. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). **/ template CImg get_resize(const CImg& src, const int interp=1, const int border_condition=-1) const { return get_resize(src.width,src.height,src.depth,src.dim,interp,border_condition); } //! In-place version of the previous function. template CImg& resize(const CImg& src, const int interp=1, const int border_condition=-1) { return resize(src.width,src.height,src.depth,src.dim,interp,border_condition); } //! Return a resized image. /** \param disp = Display giving the geometry of the resize. \param interp = Resizing type : - 0 = no interpolation : additional space is filled with 0. - 1 = bloc interpolation (nearest point). - 2 = mosaic : image is repeated if necessary. - 3 = linear interpolation. - 4 = grid interpolation. - 5 = bi-cubic interpolation. - 6 = moving average (best quality for photographs) \param border_condition Border condition type. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100). **/ CImg get_resize(const CImgDisplay& disp, const int interp=1, const int border_condition=-1) const { return get_resize(disp.width,disp.height,depth,dim,interp,border_condition); } //! In-place version of the previous function. CImg& resize(const CImgDisplay& disp, const int interp=1, const int border_condition=-1) { return resize(disp.width,disp.height,depth,dim,interp,border_condition); } //! Permute axes order. /** This function permutes image axes. \param permut = String describing the permutation (4 characters). **/ CImg get_permute_axes(const char *permut="vxyz") const { if (is_empty() || !permut) return (+*this); CImg res; const T* ptr = data; if (!cimg::strncasecmp(permut,"xyzv",4)) return (+*this); if (!cimg::strncasecmp(permut,"xyvz",4)) { res.assign(width,height,dim,depth); cimg_forXYZV(*this,x,y,z,v) res(x,y,v,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"xzyv",4)) { res.assign(width,depth,height,dim); cimg_forXYZV(*this,x,y,z,v) res(x,z,y,v) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"xzvy",4)) { res.assign(width,depth,dim,height); cimg_forXYZV(*this,x,y,z,v) res(x,z,v,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"xvyz",4)) { res.assign(width,dim,height,depth); cimg_forXYZV(*this,x,y,z,v) res(x,v,y,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"xvzy",4)) { res.assign(width,dim,depth,height); cimg_forXYZV(*this,x,y,z,v) res(x,v,z,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yxzv",4)) { res.assign(height,width,depth,dim); cimg_forXYZV(*this,x,y,z,v) res(y,x,z,v) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yxvz",4)) { res.assign(height,width,dim,depth); cimg_forXYZV(*this,x,y,z,v) res(y,x,v,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yzxv",4)) { res.assign(height,depth,width,dim); cimg_forXYZV(*this,x,y,z,v) res(y,z,x,v) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yzvx",4)) { res.assign(height,depth,dim,width); cimg_forXYZV(*this,x,y,z,v) res(y,z,v,x) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yvxz",4)) { res.assign(height,dim,width,depth); cimg_forXYZV(*this,x,y,z,v) res(y,v,x,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"yvzx",4)) { res.assign(height,dim,depth,width); cimg_forXYZV(*this,x,y,z,v) res(y,v,z,x) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zxyv",4)) { res.assign(depth,width,height,dim); cimg_forXYZV(*this,x,y,z,v) res(z,x,y,v) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zxvy",4)) { res.assign(depth,width,dim,height); cimg_forXYZV(*this,x,y,z,v) res(z,x,v,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zyxv",4)) { res.assign(depth,height,width,dim); cimg_forXYZV(*this,x,y,z,v) res(z,y,x,v) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zyvx",4)) { res.assign(depth,height,dim,width); cimg_forXYZV(*this,x,y,z,v) res(z,y,v,x) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zvxy",4)) { res.assign(depth,dim,width,height); cimg_forXYZV(*this,x,y,z,v) res(z,v,x,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"zvyx",4)) { res.assign(depth,dim,height,width); cimg_forXYZV(*this,x,y,z,v) res(z,v,y,x) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vxyz",4)) { res.assign(dim,width,height,depth); cimg_forXYZV(*this,x,y,z,v) res(v,x,y,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vxzy",4)) { res.assign(dim,width,depth,height); cimg_forXYZV(*this,x,y,z,v) res(v,x,z,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vyxz",4)) { res.assign(dim,height,width,depth); cimg_forXYZV(*this,x,y,z,v) res(v,y,x,z) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vyzx",4)) { res.assign(dim,height,depth,width); cimg_forXYZV(*this,x,y,z,v) res(v,y,z,x) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vzxy",4)) { res.assign(dim,depth,width,height); cimg_forXYZV(*this,x,y,z,v) res(v,z,x,y) = *(ptr++); return res; } if (!cimg::strncasecmp(permut,"vzyx",4)) { res.assign(dim,depth,height,width); cimg_forXYZV(*this,x,y,z,v) res(v,z,y,x) = *(ptr++); return res; } throw CImgArgumentException("CImg<%s>::permute_axes() : Invalid input permutation '%s'.",pixel_type(),permut); return res; } //! In-place version of the previous function. CImg& permute_axes(const char *order="vxyz") { return get_permute_axes(order).assign_to(*this); } //! Return an half-resized image, using a special filter. CImg get_resize_halfXY() const { typedef typename cimg::superset::type ftype; if (is_empty()) return CImg(); const ftype mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f, 0.1231940459f, 0.1935127547f, 0.1231940459f, 0.07842776544f, 0.1231940459f, 0.07842776544f }; ftype I[9] = { 0 }; CImg dest(width/2,height/2,depth,dim); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) if (x%2 && y%2) dest(x/2,y/2,z,k) = (T) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); return dest; } //! In-place version of the previous function. CImg& resize_halfXY() { return get_resize_halfXY().assign_to(*this); } //! Mirror an image along the specified axis. CImg get_mirror(const char axe='x') const { return (+*this).mirror(axe); } //! In-place version of the previous function. CImg& mirror(const char axe='x') { if (!is_empty()) { T *pf, *pb, *buf = 0; switch (cimg::uncase(axe)) { case 'x': { pf = data; pb = ptr(width-1); for (unsigned int yzv=0; yzv::mirror() : unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); } if (buf) delete[] buf; } return *this; } //! Translate the image. /** \param deltax Amount of displacement along the X-axis. \param deltay Amount of displacement along the Y-axis. \param deltaz Amount of displacement along the Z-axis. \param deltav Amount of displacement along the V-axis. \param border_condition Border condition. - \c border_condition can be : - 0 : Zero border condition (Dirichlet). - 1 : Nearest neighbors (Neumann). - 2 : Repeat Pattern (Fourier style). **/ CImg get_translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, const int border_condition=0) const { return (+*this).translate(deltax,deltay,deltaz,deltav,border_condition); } //! In-place version of the previous function. CImg& translate(const int deltax, const int deltay=0, const int deltaz=0, const int deltav=0, const int border_condition=0) { if (!is_empty()) { if (deltax) // Translate along X-axis switch (border_condition) { case 0: if (cimg::abs(deltax)>=dimx()) return fill(0); if (deltax>0) cimg_forYZV(*this,y,z,k) { std::memmove(ptr(0,y,z,k),ptr(deltax,y,z,k),(width-deltax)*sizeof(T)); std::memset(ptr(width-deltax,y,z,k),0,deltax*sizeof(T)); } else cimg_forYZV(*this,y,z,k) { std::memmove(ptr(-deltax,y,z,k),ptr(0,y,z,k),(width+deltax)*sizeof(T)); std::memset(ptr(0,y,z,k),0,-deltax*sizeof(T)); } break; case 1: if (deltax>0) { const int ndeltax = (deltax>=dimx())?width-1:deltax; if (!ndeltax) return *this; cimg_forYZV(*this,y,z,k) { std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); T *ptrd = ptr(width-1,y,z,k); const T &val = *ptrd; for (int l=0; l=dimx())?width-1:-deltax; if (!ndeltax) return *this; cimg_forYZV(*this,y,z,k) { std::memmove(ptr(ndeltax,y,z,k),ptr(0,y,z,k),(width-ndeltax)*sizeof(T)); T *ptrd = ptr(0,y,z,k); const T &val = *ptrd; for (int l=0; l0) cimg_forYZV(*this,y,z,k) { std::memcpy(buf,ptr(0,y,z,k),ndeltax*sizeof(T)); std::memmove(ptr(0,y,z,k),ptr(ndeltax,y,z,k),(width-ndeltax)*sizeof(T)); std::memcpy(ptr(width-ndeltax,y,z,k),buf,ndeltax*sizeof(T)); } else cimg_forYZV(*this,y,z,k) { std::memcpy(buf,ptr(width+ndeltax,y,z,k),-ndeltax*sizeof(T)); std::memmove(ptr(-ndeltax,y,z,k),ptr(0,y,z,k),(width+ndeltax)*sizeof(T)); std::memcpy(ptr(0,y,z,k),buf,-ndeltax*sizeof(T)); } delete[] buf; } break; } if (deltay) // Translate along Y-axis switch (border_condition) { case 0: if (cimg::abs(deltay)>=dimy()) return fill(0); if (deltay>0) cimg_forZV(*this,z,k) { std::memmove(ptr(0,0,z,k),ptr(0,deltay,z,k),width*(height-deltay)*sizeof(T)); std::memset(ptr(0,height-deltay,z,k),0,width*deltay*sizeof(T)); } else cimg_forZV(*this,z,k) { std::memmove(ptr(0,-deltay,z,k),ptr(0,0,z,k),width*(height+deltay)*sizeof(T)); std::memset(ptr(0,0,z,k),0,-deltay*width*sizeof(T)); } break; case 1: if (deltay>0) { const int ndeltay = (deltay>=dimy())?height-1:deltay; if (!ndeltay) return *this; cimg_forZV(*this,z,k) { std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); T *ptrd = ptr(0,height-ndeltay,z,k), *ptrs = ptr(0,height-1,z,k); for (int l=0; l=dimy())?height-1:-deltay; if (!ndeltay) return *this; cimg_forZV(*this,z,k) { std::memmove(ptr(0,ndeltay,z,k),ptr(0,0,z,k),width*(height-ndeltay)*sizeof(T)); T *ptrd = ptr(0,1,z,k), *ptrs = ptr(0,0,z,k); for (int l=0; l0) cimg_forZV(*this,z,k) { std::memcpy(buf,ptr(0,0,z,k),width*ndeltay*sizeof(T)); std::memmove(ptr(0,0,z,k),ptr(0,ndeltay,z,k),width*(height-ndeltay)*sizeof(T)); std::memcpy(ptr(0,height-ndeltay,z,k),buf,width*ndeltay*sizeof(T)); } else cimg_forZV(*this,z,k) { std::memcpy(buf,ptr(0,height+ndeltay,z,k),-ndeltay*width*sizeof(T)); std::memmove(ptr(0,-ndeltay,z,k),ptr(0,0,z,k),width*(height+ndeltay)*sizeof(T)); std::memcpy(ptr(0,0,z,k),buf,-ndeltay*width*sizeof(T)); } delete[] buf; } break; } if (deltaz) // Translate along Z-axis switch (border_condition) { case 0: if (cimg::abs(deltaz)>=dimz()) return fill(0); if (deltaz>0) cimg_forV(*this,k) { std::memmove(ptr(0,0,0,k),ptr(0,0,deltaz,k),width*height*(depth-deltaz)*sizeof(T)); std::memset(ptr(0,0,depth-deltaz,k),0,width*height*deltaz*sizeof(T)); } else cimg_forV(*this,k) { std::memmove(ptr(0,0,-deltaz,k),ptr(0,0,0,k),width*height*(depth+deltaz)*sizeof(T)); std::memset(ptr(0,0,0,k),0,-deltaz*width*height*sizeof(T)); } break; case 1: if (deltaz>0) { const int ndeltaz = (deltaz>=dimz())?depth-1:deltaz; if (!ndeltaz) return *this; cimg_forV(*this,k) { std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); T *ptrd = ptr(0,0,depth-ndeltaz,k), *ptrs = ptr(0,0,depth-1,k); for (int l=0; l=dimz())?depth-1:-deltaz; if (!ndeltaz) return *this; cimg_forV(*this,k) { std::memmove(ptr(0,0,ndeltaz,k),ptr(0,0,0,k),width*height*(depth-ndeltaz)*sizeof(T)); T *ptrd = ptr(0,0,1,k), *ptrs = ptr(0,0,0,k); for (int l=0; l0) cimg_forV(*this,k) { std::memcpy(buf,ptr(0,0,0,k),width*height*ndeltaz*sizeof(T)); std::memmove(ptr(0,0,0,k),ptr(0,0,ndeltaz,k),width*height*(depth-ndeltaz)*sizeof(T)); std::memcpy(ptr(0,0,depth-ndeltaz,k),buf,width*height*ndeltaz*sizeof(T)); } else cimg_forV(*this,k) { std::memcpy(buf,ptr(0,0,depth+ndeltaz,k),-ndeltaz*width*height*sizeof(T)); std::memmove(ptr(0,0,-ndeltaz,k),ptr(0,0,0,k),width*height*(depth+ndeltaz)*sizeof(T)); std::memcpy(ptr(0,0,0,k),buf,-ndeltaz*width*height*sizeof(T)); } delete[] buf; } break; } if (deltav) // Translate along V-axis switch (border_condition) { case 0: if (cimg::abs(deltav)>=dimv()) return fill(0); if (deltav>0) { std::memmove(data,ptr(0,0,0,deltav),width*height*depth*(dim-deltav)*sizeof(T)); std::memset(ptr(0,0,0,dim-deltav),0,width*height*depth*deltav*sizeof(T)); } else cimg_forV(*this,k) { std::memmove(ptr(0,0,0,-deltav),data,width*height*depth*(dim+deltav)*sizeof(T)); std::memset(data,0,-deltav*width*height*depth*sizeof(T)); } break; case 1: if (deltav>0) { const int ndeltav = (deltav>=dimv())?dim-1:deltav; if (!ndeltav) return *this; std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); T *ptrd = ptr(0,0,0,dim-ndeltav), *ptrs = ptr(0,0,0,dim-1); for (int l=0; l=dimv())?dim-1:-deltav; if (!ndeltav) return *this; std::memmove(ptr(0,0,0,ndeltav),data,width*height*depth*(dim-ndeltav)*sizeof(T)); T *ptrd = ptr(0,0,0,1); for (int l=0; l0) { std::memcpy(buf,data,width*height*depth*ndeltav*sizeof(T)); std::memmove(data,ptr(0,0,0,ndeltav),width*height*depth*(dim-ndeltav)*sizeof(T)); std::memcpy(ptr(0,0,0,dim-ndeltav),buf,width*height*depth*ndeltav*sizeof(T)); } else { std::memcpy(buf,ptr(0,0,0,dim+ndeltav),-ndeltav*width*height*depth*sizeof(T)); std::memmove(ptr(0,0,0,-ndeltav),data,width*height*depth*(dim+ndeltav)*sizeof(T)); std::memcpy(data,buf,-ndeltav*width*height*depth*sizeof(T)); } delete[] buf; } break; } } return *this; } //! Return a square region of the image, as a new image /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param z0 = Z-coordinate of the upper-left crop rectangle corner. \param v0 = V-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param z1 = Z-coordinate of the lower-right crop rectangle corner. \param v1 = V-coordinate of the lower-right crop rectangle corner. \param border_condition = Dirichlet (false) or Neumann border conditions. **/ CImg get_crop(const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const bool border_condition=false) const { if (is_empty()) return *this; const int nx0 = x0=dimx() || ny0<0 || ny1>=dimy() || nz0<0 || nz1>=dimz() || nv0<0 || nv1>=dimv()) { if (border_condition) cimg_forXYZV(dest,x,y,z,v) dest(x,y,z,v) = pix4d(nx0+x,ny0+y,nz0+z,nv0+v); else dest.fill(0).draw_image(*this,-nx0,-ny0,-nz0,-nv0); } else dest.draw_image(*this,-nx0,-ny0,-nz0,-nv0); return dest; } //! In-place version of the previous function. CImg& crop(const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1, const bool border_condition=false) { return get_crop(x0,y0,z0,v0,x1,y1,z1,v1,border_condition).assign_to(*this); } //! Return a square region of the image, as a new image /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param z0 = Z-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param z1 = Z-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg get_crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool border_condition=false) const { return get_crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); } //! In-place version of the previous function. CImg& crop(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const bool border_condition=false) { return crop(x0,y0,z0,0,x1,y1,z1,dim-1,border_condition); } //! Return a square region of the image, as a new image /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param y0 = Y-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param y1 = Y-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg get_crop(const int x0, const int y0, const int x1, const int y1, const bool border_condition=false) const { return get_crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); } //! In-place version of the previous function. CImg& crop(const int x0, const int y0, const int x1, const int y1, const bool border_condition=false) { return crop(x0,y0,0,0,x1,y1,depth-1,dim-1,border_condition); } //! Return a square region of the image, as a new image /** \param x0 = X-coordinate of the upper-left crop rectangle corner. \param x1 = X-coordinate of the lower-right crop rectangle corner. \param border_condition = determine the type of border condition if some of the desired region is outside the image. **/ CImg get_crop(const int x0, const int x1, const bool border_condition=false) const { return get_crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); } //! In-place version of the previous function. CImg& crop(const int x0, const int x1, const bool border_condition=false) { return crop(x0,0,0,0,x1,height-1,depth-1,dim-1,border_condition); } //! Return a set of columns CImg get_columns(const unsigned int x0, const unsigned int x1) const { return get_crop((int)x0,0,0,0,(int)x1,dimy()-1,dimz()-1,dimv()-1); } //! In-place version of the previous function. CImg& columns(const unsigned int x0, const unsigned int x1) { return get_columns(x0,x1).assign_to(*this); } //! Return one column CImg get_column(const unsigned int x0) const { return get_columns(x0,x0); } //! In-place version of the previous function. CImg& column(const unsigned int x0) { return columns(x0,x0); } //! Get a copy of a set of lines of the instance image. CImg get_lines(const unsigned int y0, const unsigned int y1) const { return get_crop(0,(int)y0,0,0,dimx()-1,(int)y1,dimz()-1,dimv()-1); } //! In-place version of the previous function. CImg& lines(const unsigned int y0, const unsigned int y1) { return get_lines(y0,y1).assign_to(*this); } //! Get a copy of a line of the instance image. CImg get_line(const unsigned int y0) const { return get_lines(y0,y0); } //! In-place version of the previous function. CImg& line(const unsigned int y0) { return lines(y0,y0); } //! Get a set of slices CImg get_slices(const unsigned int z0, const unsigned int z1) const { return get_crop(0,0,(int)z0,0,dimx()-1,dimy()-1,(int)z1,dimv()-1); } //! In-place version of the previous function. CImg& slices(const unsigned int z0, const unsigned int z1) { return get_slices(z0,z1).assign_to(*this); } //! Get the z-slice \a z of *this, as a new image. CImg get_slice(const unsigned int z0) const { return get_slices(z0,z0); } //! In-place version of the previous function. CImg& slice(const unsigned int z0) { return slices(z0,z0); } //! Return a copy of a set of channels of the instance image. CImg get_channels(const unsigned int v0, const unsigned int v1) const { return get_crop(0,0,0,(int)v0,dimx()-1,dimy()-1,dimz()-1,(int)v1); } //! In-place version of the previous function. CImg& channels(const unsigned int v0, const unsigned int v1) { return get_channels(v0,v1).assign_to(*this); } //! Return a copy of a channel of the instance image. CImg get_channel(const unsigned int v0) const { return get_channels(v0,v0); } //! In-place version of the previous function. CImg& channel(const unsigned int v0) { return channels(v0,v0); } //! Get a shared-memory image referencing a set of points of the instance image. CImg get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) { const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); return CImg(data+beg,x1-x0+1,1,1,1,true); } //! Get a shared-memory image referencing a set of points of the instance image (const version). const CImg get_shared_points(const unsigned int x0, const unsigned int x1, const unsigned int y0=0, const unsigned int z0=0, const unsigned int v0=0) const { const unsigned long beg = offset(x0,y0,z0,v0), end = offset(x1,y0,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_points() : Cannot return a shared-memory subset (%u->%u,%u,%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),x0,x1,y0,z0,v0,width,height,depth,dim); return CImg(data+beg,x1-x0+1,1,1,1,true); } //! Return a shared-memory image referencing a set of lines of the instance image. CImg get_shared_lines(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) { const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); return CImg(data+beg,width,y1-y0+1,1,1,true); } //! Return a shared-memory image referencing a set of lines of the instance image (const version). const CImg get_shared_lines(const unsigned int y0, const unsigned int y1, const unsigned int z0=0, const unsigned int v0=0) const { const unsigned long beg = offset(0,y0,z0,v0), end = offset(0,y1,z0,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_lines() : Cannot return a shared-memory subset (0->%u,%u->%u,%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,y0,y1,z0,v0,width,height,depth,dim); return CImg(data+beg,width,y1-y0+1,1,1,true); } //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image. CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) { return get_shared_lines(y0,y0,z0,v0); } //! Return a shared-memory image referencing one particular line (y0,z0,v0) of the instance image (const version). const CImg get_shared_line(const unsigned int y0, const unsigned int z0=0, const unsigned int v0=0) const { return get_shared_lines(y0,y0,z0,v0); } //! Return a shared memory image referencing a set of planes (z0->z1,v0) of the instance image. CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) { const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); return CImg(data+beg,width,height,z1-z0+1,1,true); } //! Return a shared-memory image referencing a set of planes (z0->z1,v0) of the instance image (const version). const CImg get_shared_planes(const unsigned int z0, const unsigned int z1, const unsigned int v0=0) const { const unsigned long beg = offset(0,0,z0,v0), end = offset(0,0,z1,v0); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_planes() : Cannot return a shared-memory subset (0->%u,0->%u,%u->%u,%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,z0,z1,v0,width,height,depth,dim); return CImg(data+beg,width,height,z1-z0+1,1,true); } //! Return a shared-memory image referencing one plane (z0,v0) of the instance image. CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) { return get_shared_planes(z0,z0,v0); } //! Return a shared-memory image referencing one plane (z0,v0) of the instance image (const version). const CImg get_shared_plane(const unsigned int z0, const unsigned int v0=0) const { return get_shared_planes(z0,z0,v0); } //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image. CImg get_shared_channels(const unsigned int v0, const unsigned int v1) { const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); return CImg(data+beg,width,height,depth,v1-v0+1,true); } //! Return a shared-memory image referencing a set of channels (v0->v1) of the instance image (const version). const CImg get_shared_channels(const unsigned int v0, const unsigned int v1) const { const unsigned long beg = offset(0,0,0,v0), end = offset(0,0,0,v1); if (beg>end || beg>=size() || end>=size()) throw CImgArgumentException("CImg<%s>::get_shared_channels() : Cannot return a shared-memory subset (0->%u,0->%u,0->%u,%u->%u) from " "a (%u,%u,%u,%u) image.",pixel_type(),width-1,height-1,depth-1,v0,v1,width,height,depth,dim); return CImg(data+beg,width,height,depth,v1-v0+1,true); } //! Return a shared-memory image referencing one channel v0 of the instance image. CImg get_shared_channel(const unsigned int v0) { return get_shared_channels(v0,v0); } //! Return a shared-memory image referencing one channel v0 of the instance image (const version). const CImg get_shared_channel(const unsigned int v0) const { return get_shared_channels(v0,v0); } //! Return a shared version of the instance image. CImg get_shared() { return CImg(data,width,height,depth,dim,true); } //! Return a shared version of the instance image (const version). const CImg get_shared() const { return CImg(data,width,height,depth,dim,true); } //! Return a 2D representation of a 3D image, with three slices. CImg get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, const int dx=-100, const int dy=-100, const int dz=-100) const { if (is_empty()) return CImg(); const unsigned int nx0 = (x0>=width)?width-1:x0, ny0 = (y0>=height)?height-1:y0, nz0 = (z0>=depth)?depth-1:z0; CImg imgxy(width,height,1,dim), imgzy(depth,height,1,dim), imgxz(width,depth,1,dim); { cimg_forXYV(*this,x,y,k) imgxy(x,y,k) = (*this)(x,y,nz0,k); } { cimg_forYZV(*this,y,z,k) imgzy(z,y,k) = (*this)(nx0,y,z,k); } { cimg_forXZV(*this,x,z,k) imgxz(x,z,k) = (*this)(x,ny0,z,k); } imgxy.resize(dx,dy,1,dim,1); imgzy.resize(dz,dy,1,dim,1); imgxz.resize(dx,dz,1,dim,1); return CImg(imgxy.width+imgzy.width,imgxy.height+imgxz.height,1,dim,0). draw_image(imgxy,0,0). draw_image(imgzy,imgxy.width,0). draw_image(imgxz,0,imgxy.height); } //! In-place version of the previous function. CImg& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0, const int dx=-100, const int dy=-100, const int dz=-100) { return get_projections2d(x0,y0,z0,dx,dy,dz).assign_to(*this); } //! Return the image histogram. /** The histogram H of an image I is a 1D-function where H(x) is the number of occurences of the value x in I. \param nblevels = Number of different levels of the computed histogram. For classical images, this value is 256 (default value). You should specify more levels if you are working with CImg or images with high range of pixel values. \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min won't be counted. \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max won't be counted. \note If val_min==val_max==0 (default values), the function first estimates the minimum and maximum pixel values of the current image, then uses these values for the histogram computation. \result The histogram is returned as a 1D CImg image H, having a size of (nblevels,1,1,1) such that H(0) and H(nblevels-1) are respectively equal to the number of occurences of the values val_min and val_max in I. \note Histogram computation always returns a 1D function. Histogram of multi-valued (such as color) images are not multi-dimensional. \see get_equalize_histogram(), equalize_histogram() **/ CImg::type> get_histogram(const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) const { typedef typename cimg::last::type ftype; if (is_empty()) return CImg(); if (!nblevels) throw CImgArgumentException("CImg<%s>::get_histogram() : Can't compute an histogram with 0 levels", pixel_type()); T vmin = val_min, vmax = val_max; CImg res(nblevels,1,1,1,0); if (vmin>=vmax && vmin==0) vmin = minmax(vmax); if (vmin=0 && pos<(int)nblevels) ++res[pos]; } else res[0]+=size(); return res; } //! In-place version of the previous function. CImg& histogram(const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) { return get_histogram(nblevels,val_min,val_max).assign_to(*this); } //! Return the histogram-equalized version of the current image. /** The histogram equalization is a classical image processing algorithm that enhances the image contrast by expanding its histogram. \param nblevels = Number of different levels of the computed histogram. For classical images, this value is 256 (default value). You should specify more levels if you are working with CImg or images with high range of pixel values. \param val_min = Minimum value considered for the histogram computation. All pixel values lower than val_min won't be changed. \param val_max = Maximum value considered for the histogram computation. All pixel values higher than val_max won't be changed. \note If val_min==val_max==0 (default values), the function acts on all pixel values of the image. \return A new image with same size is returned, where pixels have been equalized. \see get_histogram(), equalize_histogram() **/ CImg get_equalize_histogram(const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) const { return (+*this).equalize_histogram(nblevels,val_min,val_max); } //! In-place version of the previous function. CImg& equalize_histogram(const unsigned int nblevels=256, const T val_min=(T)0, const T val_max=(T)0) { if (!is_empty()) { T vmin = val_min, vmax = val_max; if (vmin==vmax && vmin==0) vmin = minmax(vmax); if (vmin hist = get_histogram(nblevels,vmin,vmax); float cumul = 0; cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos]=cumul; } cimg_for(*this,ptr,T) { const int pos = (unsigned int)((*ptr-vmin)*(nblevels-1)/(vmax-vmin)); if (pos>=0 && pos<(int)nblevels) *ptr = (T)(vmin + (vmax-vmin)*hist[pos]/size()); } } } return *this; } //! Get a label map of disconnected regions with same intensities. CImg::type> get_label_regions() const { #define _cimg_get_label_test(p,q) { \ flag = true; \ const T *ptr1 = ptr(x,y) + siz, *ptr2 = ptr(p,q) + siz; \ for (unsigned int i = dim; flag && i; --i) { ptr1-=wh; ptr2-=wh; flag = (*ptr1==*ptr2); } \ } if (depth>1) throw CImgInstanceException("CImg<%s>::label_regions() : Instance image must be a 2D image"); typedef typename cimg::last::type uitype; CImg res(width,height,depth,1,0); unsigned int label = 1; const unsigned int wh = width*height, siz = width*height*dim; const int W1 = dimx()-1, H1 = dimy()-1; bool flag; cimg_forXY(*this,x,y) { bool done = false; if (y) { _cimg_get_label_test(x,y-1); if (flag) { const unsigned int lab = (res(x,y) = res(x,y-1)); done = true; if (x && res(x-1,y)!=lab) { _cimg_get_label_test(x-1,y); if (flag) { const unsigned int lold = res(x-1,y), *const cptr = res.ptr(x,y); for (unsigned int *ptr = res.ptr(); ptr=0; --y) for (int x=W1; x>=0; --x) { bool done = false; if (ycptr; --ptr) if (*ptr==lold) *ptr = lab; } } } } if (x1), this function computes the L1,L2 or Linf norm of each vector-valued pixel. \param norm_type = Type of the norm being computed (1 = L1, 2 = L2, -1 = Linf). \return A scalar-valued image CImg with size (dimx(),dimy(),dimz(),1), where each pixel is the norm of the corresponding pixels in the original vector-valued image. \see get_orientation_pointwise, orientation_pointwise, norm_pointwise. **/ CImg::type> get_norm_pointwise(int norm_type=2) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); CImg res(width,height,depth); switch(norm_type) { case -1: // Linf norm { cimg_forXYZ(*this,x,y,z) { restype n = 0; cimg_forV(*this,v) { const restype tmp = (restype)cimg::abs((*this)(x,y,z,v)); if (tmp>n) n=tmp; res(x,y,z) = n; } } } break; case 1: // L1 norm { cimg_forXYZ(*this,x,y,z) { restype n = 0; cimg_forV(*this,v) n+=cimg::abs((*this)(x,y,z,v)); res(x,y,z) = n; } } break; default: // L2 norm { cimg_forXYZ(*this,x,y,z) { restype n = 0; cimg_forV(*this,v) n+=(*this)(x,y,z,v)*(*this)(x,y,z,v); res(x,y,z) = (restype)std::sqrt((double)n); } } break; } return res; } //! In-place version of the previous function. CImg& norm_pointwise(int norm_type=2) { return get_norm_pointwise(norm_type).assign_to(*this); } //! Return the image of normalized vectors /** When dealing with vector-valued images (i.e images with dimv()>1), this function return the image of normalized vectors (unit vectors). Null vectors are unchanged. The L2-norm is computed for the normalization. \return A new vector-valued image with same size, where each vector-valued pixels have been normalized. \see get_norm_pointwise, norm_pointwise, orientation_pointwise. **/ CImg::type> get_orientation_pointwise() const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); return CImg(*this,false).orientation_pointwise(); } //! In-place version of the previous function. CImg& orientation_pointwise() { cimg_forXYZ(*this,x,y,z) { float n = 0.0f; cimg_forV(*this,v) n+=(float)((*this)(x,y,z,v)*(*this)(x,y,z,v)); n = (float)std::sqrt(n); if (n>0) cimg_forV(*this,v) (*this)(x,y,z,v) = (T)((*this)(x,y,z,v)/n); else cimg_forV(*this,v) (*this)(x,y,z,v) = 0; } return *this; } //! Split image into a list. CImgList get_split(const char axe='x', const unsigned int nb=0) const { if (is_empty()) return CImgList(); CImgList res; switch (cimg::uncase(axe)) { case 'x': { if (nb>width) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'x' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:width); const unsigned int delta = width/res.size + ((width%res.size)?1:0); unsigned int l,x; for (l=0, x=0; lheight) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'y' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:height); const unsigned int delta = height/res.size + ((height%res.size)?1:0); unsigned int l,x; for (l=0, x=0; ldepth) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'z' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:depth); const unsigned int delta = depth/res.size + ((depth%res.size)?1:0); unsigned int l,x; for (l=0, x=0; ldim) throw CImgArgumentException("CImg<%s>::get_split() : Cannot split instance image (%u,%u,%u,%u,%p) along 'v' into %u images.", pixel_type(),width,height,depth,dim,data,nb); res.assign(nb?nb:dim); const unsigned int delta = dim/res.size + ((dim%res.size)?1:0); unsigned int l,x; for (l=0, x=0; l::get_split() : Unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); break; } return res; } //! Append an image to another one CImg get_append(const CImg& img, const char axis='x', const char align='c') const { if (!img) return *this; if (is_empty()) return img; CImgList temp(2); temp[0].width = width; temp[0].height = height; temp[0].depth = depth; temp[0].dim = dim; temp[0].data = data; temp[1].width = img.width; temp[1].height = img.height; temp[1].depth = img.depth; temp[1].dim = img.dim; temp[1].data = img.data; const CImg res = temp.get_append(axis,align); temp[0].width = temp[0].height = temp[0].depth = temp[0].dim = 0; temp[0].data = 0; temp[1].width = temp[1].height = temp[1].depth = temp[1].dim = 0; temp[1].data = 0; return res; } //! In-place version of the previous function. CImg& append(const CImg& img, const char axis='x', const char align='c') { if (!img) return *this; if (is_empty()) return (*this=img); return get_append(img,axis,align).assign_to(*this); } //! Return a list of images, corresponding to the XY-gradients of an image. /** \param scheme = Numerical scheme used for the gradient computation : - -1 = Backward finite differences - 0 = Centered finite differences - 1 = Forward finite differences - 2 = Using Sobel masks - 3 = Using rotation invariant masks - 4 = Using Deriche recusrsive filter. **/ CImgList::type> get_gradientXY(const int scheme=0) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImgList(2); CImgList res(2,width,height,depth,dim); switch(scheme) { case -1: // backward finite differences { CImg_3x3(I,restype); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Icc-Ipc; res[1](x,y,z,k) = Icc-Icp; } } break; case 1: // forward finite differences { CImg_2x2(I,restype); cimg_forZV(*this,z,k) cimg_for2x2(*this,x,y,z,k,I) { res[0](x,y,0,k) = Inc-Icc; res[1](x,y,z,k) = Icn-Icc; } } break; case 2: // using Sobel mask { CImg_3x3(I,restype); const float a = 1, b = 2; cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = -a*Ipp-b*Ipc-a*Ipn+a*Inp+b*Inc+a*Inn; res[1](x,y,z,k) = -a*Ipp-b*Icp-a*Inp+a*Ipn+b*Icn+a*Inn; } } break; case 3: // using rotation invariant mask { CImg_3x3(I,restype); const float a = (float)(0.25*(2-std::sqrt(2.0))), b = (float)(0.5f*(std::sqrt(2.0)-1)); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = -a*Ipp-b*Ipc-a*Ipn+a*Inp+b*Inc+a*Inn; res[1](x,y,z,k) = -a*Ipp-b*Icp-a*Inp+a*Ipn+b*Icn+a*Inn; } } break; case 4: // using Deriche filter with low standard variation { res[0] = get_deriche(0,1,'x'); res[1] = get_deriche(0,1,'y'); } break; default: // central finite differences { CImg_3x3(I,restype); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = 0.5f*(Inc-Ipc); res[1](x,y,z,k) = 0.5f*(Icn-Icp); } } break; } return res; } //! Return a list of images, corresponding to the XYZ-gradients of an image. /** \see get_gradientXY(). **/ CImgList::type> get_gradientXYZ(const int scheme=0) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImgList(3); CImgList res(3,width,height,depth,dim); CImg_3x3x3(I,restype); switch(scheme) { case -1: // backward finite differences { cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Iccc-Ipcc; res[1](x,y,z,k) = Iccc-Icpc; res[2](x,y,z,k) = Iccc-Iccp; } } break; case 1: // forward finite differences { cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Incc-Iccc; res[1](x,y,z,k) = Icnc-Iccc; res[2](x,y,z,k) = Iccn-Iccc; } } break; case 4: // using Deriche filter with low standard variation { res[0] = get_deriche(0,1,'x'); res[1] = get_deriche(0,1,'y'); res[2] = get_deriche(0,1,'z'); } break; default: // central finite differences { cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = 0.5f*(Incc-Ipcc); res[1](x,y,z,k) = 0.5f*(Icnc-Icpc); res[2](x,y,z,k) = 0.5f*(Iccn-Iccp); } } break; } return res; } //! Return the 2D structure tensor field of an image CImg::type> get_structure_tensorXY(const int scheme=1) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); CImg res(width,height,depth,3,0); CImg_3x3(I,restype); switch (scheme) { case 0: // classical central finite differences { cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,0,k,I) { const restype ix = 0.5f*(Inc-Ipc), iy = 0.5f*(Icn-Icp); res(x,y,z,0)+=ix*ix; res(x,y,z,1)+=ix*iy; res(x,y,z,2)+=iy*iy; } } break; default: // Precise forward/backward finite differences { cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,0,k,I) { const restype ixf = Inc-Icc, ixb = Icc-Ipc, iyf = Icn-Icc, iyb = Icc-Icp; res(x,y,z,0) += 0.5f*(ixf*ixf+ixb*ixb); res(x,y,z,1) += 0.25f*(ixf*iyf+ixf*iyb+ixb*iyf+ixb*iyb); res(x,y,z,2) += 0.5f*(iyf*iyf+iyb*iyb); } } break; } return res; } //! In-place version of the previous function. CImg& structure_tensorXY(const int scheme=1) { return get_structure_tensorXY(scheme).assign_to(*this); } //! Return the 3D structure tensor field of an image CImg::type> get_structure_tensorXYZ(const int scheme=1) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); CImg res(width,height,depth,6,0); CImg_3x3x3(I,restype); switch (scheme) { case 0: // classical central finite differences { cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { const restype ix = 0.5f*(Incc-Ipcc), iy = 0.5f*(Icnc-Icpc), iz = 0.5f*(Iccn-Iccp); res(x,y,z,0)+=ix*ix; res(x,y,z,1)+=ix*iy; res(x,y,z,2)+=ix*iz; res(x,y,z,3)+=iy*iy; res(x,y,z,4)+=iy*iz; res(x,y,z,5)+=iz*iz; } } break; default: // Precise forward/backward finite differences { cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { const restype ixf = Incc-Iccc, ixb = Iccc-Ipcc, iyf = Icnc-Iccc, iyb = Iccc-Icpc, izf = Iccn-Iccc, izb = Iccc-Iccp; res(x,y,z,0) += 0.5f*(ixf*ixf + ixb*ixb); res(x,y,z,1) += 0.25f*(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb); res(x,y,z,2) += 0.25f*(ixf*izf + ixf*izb + ixb*izf + ixb*izb); res(x,y,z,3) += 0.5f*(iyf*iyf + iyb*iyb); res(x,y,z,4) += 0.25f*(iyf*izf + iyf*izb + iyb*izf + iyb*izb); res(x,y,z,5) += 0.5f*(izf*izf + izb*izb); } } break; } return res; } //! In-place version of the previous function. CImg& structure_tensorXYZ(const int scheme=1) { return get_structure_tensorXYZ(scheme).assign_to(*this); } //! Get components of the 2D Hessian matrix of an image. /** Components are ordered as : Ixx, Ixy, Iyy **/ CImgList::type> get_hessianXY() { typedef typename cimg::superset::type ftype; if (is_empty()) return CImgList(3); CImgList res(3,width,height,depth,dim); CImg_3x3(I,ftype); cimg_forZV(*this,z,k) cimg_for3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Ipc + Inc - 2*Icc; // Ixx res[1](x,y,z,k) = 0.25f*(Ipp + Inn - Ipn - Inp); // Ixy res[2](x,y,z,k) = Icp + Icn - 2*Icc; // Iyy } return res; } //! Get components of the 3D Hessian matrix of an image. /** Components are ordered as : Ixx, Ixy, Ixz, Iyy, Iyz, Izz. **/ CImgList::type> get_hessianXYZ() { typedef typename cimg::superset::type ftype; if (is_empty()) return CImgList(6); CImgList res(6,width,height,depth,dim); CImg_3x3x3(I,ftype); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { res[0](x,y,z,k) = Ipcc + Incc - 2*Iccc; // Ixx res[1](x,y,z,k) = 0.25f*(Ippc + Innc - Ipnc - Inpc); // Ixy res[2](x,y,z,k) = 0.25f*(Ipcp + Incn - Ipcn - Incp); // Ixz res[3](x,y,z,k) = Icpc + Icnc - 2*Iccc; // Iyy res[4](x,y,z,k) = 0.25f*(Icpp + Icnn - Icpn - Icnp); // Iyz res[5](x,y,z,k) = Iccn + Iccp - 2*Iccc; // Izz } return res; } //! Get distance function from 0-valued isophotes by the application of the eikonal equation. CImg::type> get_distance_function(const unsigned int nb_iter=100, const float band_size=0.0f, const float precision=0.5f) const { typedef typename cimg::superset::type ftype; return CImg(*this,false).distance_function(nb_iter,band_size,precision); } //! In-place version of the previous function. CImg& distance_function(const unsigned int nb_iter=100, const float band_size=0.0f, const float precision=0.5f) { typedef typename cimg::superset::type ftype; if (is_empty()) return *this; CImg veloc(*this); for (unsigned int iter=0; iter1) // 3D version { CImg_3x3x3(I,ftype); cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) if (band_size<=0 || cimg::abs(Iccc)0?Incc-Iccc:Iccc-Ipcc, iy = gy*sgn>0?Icnc-Iccc:Iccc-Icpc, iz = gz*sgn>0?Iccn-Iccc:Iccc-Iccp, ng = 1e-5f+(ftype)std::sqrt(gx*gx+gy*gy+gz*gz), ngx = gx/ng, ngy = gy/ng, ngz = gz/ng; veloc(x,y,z,k) = sgn*(ngx*ix+ngy*iy+ngz*iz-1); } } else // 2D version { CImg_3x3(I,ftype); cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) if (band_size<=0 || cimg::abs(Icc)0?Inc-Icc:Icc-Ipc, iy = gy*sgn>0?Icn-Icc:Icc-Icp, ng = 1e-5f+(ftype)std::sqrt(gx*gx+gy*gy), ngx = gx/ng, ngy = gy/ng; veloc(x,y,k) = sgn*(ngx*ix+ngy*iy-1); } } float m, M = (float)veloc.maxmin(m), xdt = precision/(float)cimg::max(cimg::abs(m),cimg::abs(M)); *this+=(veloc*=xdt); } return *this; } //! Return minimal path in a graph, using the Dijkstra algorithm /** \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance between two nodes (i,j). \param nb_nodes Number of graph nodes. \param starting_node Indice of the starting node. \param ending_node Indice of the ending node (set to ~0U to ignore ending node). \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). \return Array of distances of each node to the starting node. **/ template static CImg get_dijkstra(const tf& distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { CImg dist(1,nb_nodes,1,1,cimg::type::max()); dist(starting_node) = 0; previous.assign(1,nb_nodes,1,1,(t)-1); previous(starting_node) = (t)starting_node; typedef typename cimg::last::type uitype; CImg Q(nb_nodes); cimg_forX(Q,u) Q(u) = u; cimg::swap(Q(starting_node),Q(0)); unsigned int sizeQ = nb_nodes; while (sizeQ) { // Update neighbors from minimal vertex const unsigned int umin = Q(0); if (umin==ending_node) sizeQ = 0; else { const T dmin = dist(umin); const T& infty = cimg::type::max(); for (unsigned int q=1; qdist(Q(left))) || (rightdist(Q(right)));) { if (right static CImg get_dijkstra(const tf& distance, const unsigned int nb_nodes, const unsigned int starting_node, const unsigned int ending_node=~0U) { typedef typename cimg::last::type uitype; CImg foo; return get_dijkstra(distance,nb_nodes,starting_node,ending_node,foo); } //! Return minimal path in a graph, using the Dijkstra algorithm. /** Instance image corresponds to the adjacency matrix of the graph. \param starting_node Indice of the starting node. \param previous Array that gives the previous node indice in the path to the starting node (optional parameter). \return Array of distances of each node to the starting node. **/ template CImg get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) const { if (width!=height || depth!=1 || dim!=1) throw CImgInstanceException("CImg<%s>::dijkstra() : Instance image (%u,%u,%u,%u,%p) is not a graph adjacency matrix", pixel_type(),width,height,depth,dim,data); return CImg::get_dijkstra(*this,width,starting_node,ending_node,previous); } // In-place version of the previous function template CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg& previous) { return get_dijkstra(starting_node,ending_node,previous).assign_to(*this); } //! Return minimal path in a graph, using the Dijkstra algorithm CImg::type> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const { typedef typename cimg::last::type uitype; CImg foo; return get_dijkstra(starting_node,ending_node,foo); } // In-place version of the previous function CImg& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) { return get_dijkstra(starting_node,ending_node).assign_to(*this); } //@} //------------------------------------- // //! \name Meshes and Triangulations //@{ //------------------------------------- struct _marching_squares_func { const CImg& ref; _marching_squares_func(const CImg& pref):ref(pref) {} float operator()(const float x, const float y) const { return (float)ref((int)x,(int)y); } }; struct _marching_cubes_func { const CImg& ref; _marching_cubes_func(const CImg& pref):ref(pref) {} float operator()(const float x, const float y, const float z) const { return (float)ref((int)x,(int)y,(int)z); } }; struct _marching_squares_func_float { const CImg& ref; _marching_squares_func_float(const CImg& pref):ref(pref) {} float operator()(const float x, const float y) const { return (float)ref.linear_pix2d(x,y); } }; struct _marching_cubes_func_float { const CImg& ref; _marching_cubes_func_float(const CImg& pref):ref(pref) {} float operator()(const float x, const float y, const float z) const { return (float)ref.linear_pix3d(x,y,z); } }; //! Get a vectorization of an implicit function defined by the instance image. template const CImg& marching_squares(const float isovalue, CImgList& points, CImgList& primitives) const { if (height<=1 || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::marching_squares() : Instance image (%u,%u,%u,%u,%p) is not a 2D scalar image.", pixel_type(),width,height,depth,dim,data); const _marching_squares_func func(*this); cimg::marching_squares(func,isovalue,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,1.0f,1.0f,points,primitives); return *this; } //! Get a vectorization of an implicit function defined by the instance image. /** This version allows to specify the marching squares resolution along x,y, and z. **/ template const CImg& marching_squares(const float isovalue, const float resx, const float resy, CImgList& points, CImgList& primitives) const { if (height<=1 || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::marching_squares() : Instance image (%u,%u,%u,%u,%p) is not a 2D scalar image.", pixel_type(),width,height,depth,dim,data); const _marching_squares_func_float func(*this); cimg::marching_squares(func,isovalue,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,resx,resy,points,primitives); return *this; } //! Get a triangulation of an implicit function defined by the instance image template const CImg& marching_cubes(const float isovalue, CImgList& points, CImgList& primitives, const bool invert_faces = false) const { if (depth<=1 || dim>1) throw CImgInstanceException("CImg<%s>::marching_cubes() : Instance image (%u,%u,%u,%u,%p) is not a 3D scalar image.", pixel_type(),width,height,depth,dim,data); const _marching_cubes_func func(*this); cimg::marching_cubes(func,isovalue,0.0f,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f, 1.0f,1.0f,1.0f,points,primitives,invert_faces); return *this; } //! Get a triangulation of an implicit function defined by the instance image /** This version allows to specify the marching cube resolution along x,y and z. **/ template const CImg& marching_cubes(const float isovalue, const float resx, const float resy, const float resz, CImgList& points, CImgList& primitives, const bool invert_faces = false) const { if (depth<=1 || dim>1) throw CImgInstanceException("CImg<%s>::marching_cubes() : Instance image (%u,%u,%u,%u,%p) is not a 3D scalar image.", pixel_type(),width,height,depth,dim,data); const _marching_cubes_func_float func(*this); cimg::marching_cubes(func,isovalue,0.0f,0.0f,0.0f,dimx()-1.0f,dimy()-1.0f,dimz()-1.0f, resx,resy,resz,points,primitives,invert_faces); return *this; } //@} //---------------------------- // //! \name Color conversions //@{ //---------------------------- //! Return the default 256 colors palette. /** The default color palette is used by %CImg when displaying images on 256 colors displays. It consists in the quantification of the (R,G,B) color space using 3:3:2 bits for color coding (i.e 8 levels for the Red and Green and 4 levels for the Blue). \return A 256x1x1x3 color image defining the palette entries. **/ static CImg get_default_LUT8() { static CImg palette; if (!palette) { palette.assign(1,256,1,3); for (unsigned int index=0, r=16; r<256; r+=32) for (unsigned int g=16; g<256; g+=32) for (unsigned int b=32; b<256; b+=64) { palette(0,index,0) = (T)r; palette(0,index,1) = (T)g; palette(0,index++,2) = (T)b; } } return palette; } //! Return a rainbow-palette static CImg get_rainbow_LUT8() { static CImg palette; if (!palette) { palette.assign(1,256,1,3,255); palette.get_shared_channel(0).sequence(0,359); palette.HSVtoRGB(); } return palette; } //! Return contrasted palette optmized for cluster visualization static CImg get_cluster_LUT8() { static const unsigned char pal[] = { 217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226, 17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119, 238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20, 233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74, 81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219, 1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12, 87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0, 223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32, 233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4, 137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224, 4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247, 11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246, 0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10, 141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143, 116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244, 255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0, 235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251, 129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30, 243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215, 95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3, 141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174, 154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87, 33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21, 23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 }; static const CImg palette(pal,256,1,1,3); return palette; } //! Convert color pixels from (R,G,B) to match a specified palette. /** This function return a (R,G,B) image where colored pixels are constrained to match entries of the specified color \c palette. \param palette User-defined palette that will constraint the color conversion. \param dithering Enable/Disable Floyd-Steinberg dithering. \param indexing If \c true, each resulting image pixel is an index to the given color palette. Otherwise, (R,G,B) values of the palette are copied instead. **/ template CImg get_RGBtoLUT(const CImg& palette, const bool dithering=true, const bool indexing=false) const { if (is_empty()) return CImg(); if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoLUT() : Input image dimension is dim=%u, " "should be a (R,G,B) image.",pixel_type(),dim); if (palette.data && palette.dim!=3) throw CImgArgumentException("CImg<%s>::RGBtoLUT() : Given palette dimension is dim=%u, " "should be a (R,G,B) palette",pixel_type(),palette.dim); CImg res(width,height,depth,indexing?1:3), pal = palette.data?palette:CImg::get_default_LUT8(); float *line1 = new float[3*width], *line2 = new float[3*width], *pline1 = line1, *pline2 = line2; cimg_forZ(*this,z) { float *ptr = pline2; cimg_forX(*this,x) { *(ptr++) = (*this)(x,0,z,0); *(ptr++) = (*this)(x,0,z,1); *(ptr++) = (*this)(x,0,z,2); } cimg_forY(*this,y) { cimg::swap(pline1,pline2); if (y255?255:R); G = G<0?0:(G>255?255:G); B = B<0?0:(B>255?255:B); int best_index = 0; t Rbest = 0, Gbest = 0, Bbest = 0; if (palette.data) // find best match in given color palette { float min = cimg::type::max(); cimg_forX(palette,off) { const t Rp = palette(off,0), Gp = palette(off,1), Bp = palette(off,2); const float error = (float)((Rp-R)*(Rp-R) + (Gp-G)*(Gp-G) + (Bp-B)*(Bp-B)); if (error>3) | ((unsigned char)Bbest>>6); } if (indexing) res(x,y,z) = best_index; else { res(x,y,z,0) = Rbest; res(x,y,z,1) = Gbest; res(x,y,z,2) = Bbest; } if (dithering) // apply dithering to neighborhood pixels if needed { const float dR = (float)(R-Rbest), dG = (float)(G-Gbest), dB = (float)(B-Bbest); if (x0) { *(--ptr2)+= dB*3/16; *(--ptr2)+= dG*3/16; *(--ptr2)+= dR*3/16; ptr2+=3; } if (x& palette, const bool dithering=true, const bool indexing=false) { return get_RGBtoLUT(palette,dithering,indexing).assign_to(*this); } //! Convert color pixels from (R,G,B) to match the default 256 colors palette. /** Same as get_RGBtoLUT() with the default color palette given by get_default_LUT8(). **/ CImg get_RGBtoLUT(const bool dithering=true, const bool indexing=false) const { CImg foo; return get_RGBtoLUT(foo,dithering,indexing); } //! In-place version of the previous function. CImg& RGBtoLUT(const bool dithering=true, const bool indexing=false) { CImg foo; return get_RGBtoLUT(foo,dithering,indexing).assign_to(*this); } //! Convert an indexed image to a (R,G,B) image using the specified color palette. template CImg get_LUTtoRGB(const CImg& palette) const { if (is_empty()) return CImg(); if (dim!=1) throw CImgInstanceException("CImg<%s>::LUTtoRGB() : Input image dimension is dim=%u, " "should be a LUT image",pixel_type(),dim); if (palette.data && palette.dim!=3) throw CImgArgumentException("CImg<%s>::LUTtoRGB() : Given palette dimension is dim=%u, " "should be a (R,G,B) palette",pixel_type(),palette.dim); CImg res(width,height,depth,3); CImg pal = palette.data?palette:CImg::get_default_LUT8(); cimg_forXYZ(*this,x,y,z) { const unsigned int index = (unsigned int)(*this)(x,y,z); res(x,y,z,0) = pal(index,0); res(x,y,z,1) = pal(index,1); res(x,y,z,2) = pal(index,2); } return res; } //! In-place version of the previous function. CImg& LUTtoRGB(const CImg& palette) { return get_LUTtoRGB(palette).assign_to(*this); } //! Convert an indexed image (with the default palette) to a (R,G,B) image. CImg get_LUTtoRGB() const { CImg foo; return get_LUTtoRGB(foo); } //! In-place version of the previous function. CImg& LUTtoRGB() { CImg foo; return get_LUTtoRGB(foo).assign_to(*this); } //! Convert color pixels from (R,G,B) to (H,S,V). CImg::type> get_RGBtoHSV() const { typedef typename cimg::superset::type ftype; return CImg(*this).RGBtoHSV(); } //! In-place version of the previous function. CImg& RGBtoHSV() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSV() : Input image dimension is dim=%u, " "should be a (R,G,B) image.",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const float R = (float)((*this)(x,y,z,0)/255.0f), G = (float)((*this)(x,y,z,1)/255.0f), B = (float)((*this)(x,y,z,2)/255.0f); const float m = cimg::min(R,G,B), M = cimg::max(R,G,B); float H = 0, S = 0; if (M!=m) { const float f = (R==m)?(G-B):((G==m)?(B-R):(R-G)), i = (R==m)?3.0f:((G==m)?5.0f:1.0f); H = (i-f/(M-m)); if (H>=6.0f) H-=6.0f; H*=60; S = (M-m)/M; } (*this)(x,y,z,0) = (T)H; (*this)(x,y,z,1) = (T)S; (*this)(x,y,z,2) = (T)M; } } return *this; } //! Convert color pixels from (H,S,V) to (R,G,B). CImg get_HSVtoRGB() const { return (+*this).HSVtoRGB(); } //! In-place version of the previous function. CImg& HSVtoRGB() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::HSVtoRGB() : Input image dimension is dim=%u, " "should be a (H,S,V) image",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { float H = (float)((*this)(x,y,z,0)), S = (float)((*this)(x,y,z,1)), V = (float)((*this)(x,y,z,2)); float R = 0, G = 0, B = 0; if (H<0) R = G = B = V; else { H/=60.0f; const int i = (int)std::floor(H); const float f = (i&1)?(H-i):(1.0f-H+i), m = V*(1.0f-S), n = V*(1.0f-S*f); switch(i) { case 6: case 0: R = V; G = n; B = m; break; case 1: R = n; G = V; B = m; break; case 2: R = m; G = V; B = n; break; case 3: R = m; G = n; B = V; break; case 4: R = n; G = m; B = V; break; case 5: R = V; G = m; B = n; break; } } (*this)(x,y,z,0) = (T)(R*255.0f); (*this)(x,y,z,1) = (T)(G*255.0f); (*this)(x,y,z,2) = (T)(B*255.0f); } } return *this; } //! Convert color pixels from (R,G,B) to (H,S,L). CImg::type> get_RGBtoHSL() const { typedef typename cimg::superset::type ftype; return CImg(*this).RGBtoHSL(); } //! In-place version of the previous function. CImg& RGBtoHSL() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoHSL() : Input image dimension is dim=%u, " "should be a (R,G,B) image.",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const float R = (float)((*this)(x,y,z,0)/255.0f), G = (float)((*this)(x,y,z,1)/255.0f), B = (float)((*this)(x,y,z,2)/255.0f); const float m = cimg::min(R,G,B), M = cimg::max(R,G,B), L = 0.5f*(m+M); float H = 0, S = 0; if (M==m || L==0) H = S = 0; else { const float f = (R==m)?(G-B):((G==m)?(B-R):(R-G)), i = (R==m)?3.0f:((G==m)?5.0f:1.0f); H = (i-f/(M-m)); if (H>=6.0f) H-=6.0f; H*=60; S = (L<=0.5f)?((M-m)/(M+m)):((M-m)/(2-M-m)); } (*this)(x,y,z,0) = (T)H; (*this)(x,y,z,1) = (T)S; (*this)(x,y,z,2) = (T)L; } } return *this; } //! Convert color pixels from (R,G,B) to (Y,Cb,Cr)_8 (Thanks to Chen Wang). CImg get_RGBtoYCbCr() const { return (+*this).RGBtoYCbCr(); } //! In-place version of the previous function. CImg& RGBtoYCbCr() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYCbCr() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const int R = (int)((*this)(x,y,z,0)), G = (int)((*this)(x,y,z,1)), B = (int)((*this)(x,y,z,2)); const int Y = ((66*R+129*G+25*B+128)>>8) + 16, Cb = ((-38*R-74*G+112*B+128)>>8) + 128, Cr = ((112*R-94*G-18*B+128)>>8) + 128; (*this)(x,y,z,0) = (T)(Y<0?0:(Y>255?255:Y)); (*this)(x,y,z,1) = (T)(Cb<0?0:(Cb>255?255:Cb)); (*this)(x,y,z,2) = (T)(Cr<0?0:(Cr>255?255:Cr)); } } return *this; } //! Convert color pixels from (Y,Cb,Cr)_8 to (R,G,B). CImg get_YCbCrtoRGB() const { return (+*this).YCbCrtoRGB(); } //! In-place version of the previous function. CImg& YCbCrtoRGB() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::YCbCrtoRGB() : Input image dimension is dim=%u, " "should be a (Y,Cb,Cr)_8 image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const int Y = (int)((*this)(x, y, z, 0)-16), Cb = (int)((*this)(x, y, z, 1)-128), Cr = (int)((*this)(x, y, z, 2)-128); const int R = ((298*Y + 409*Cr + 128) >> 8 ), G = ((298*Y - 100*Cb - 208*Cr + 128) >> 8 ), B = ((298*Y + 516*Cb + 128) >> 8 ); (*this)(x,y,z,0) = (T)(R<0?0:(R>255?255:R)); (*this)(x,y,z,1) = (T)(G<0?0:(G>255?255:G)); (*this)(x,y,z,2) = (T)(B<0?0:(B>255?255:B)); } } return *this; } //! Convert color pixels from (R,G,B) to (Y,U,V). CImg::type> get_RGBtoYUV() const { typedef typename cimg::superset::type restype; return CImg(*this,false).RGBtoYUV(); } //! In-place version of the previous function. CImg& RGBtoYUV() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoYUV() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const float R = (*this)(x,y,z,0)/255.0f, G = (*this)(x,y,z,1)/255.0f, B = (*this)(x,y,z,2)/255.0f, Y = (T)(0.299*R + 0.587*G + 0.114*B); (*this)(x,y,z,0) = (T)Y; (*this)(x,y,z,1) = (T)(0.492*(B-Y)); (*this)(x,y,z,2) = (T)(0.877*(R-Y)); } } return *this; } //! Convert color pixels from (Y,U,V) to (R,G,B). CImg get_YUVtoRGB() const { return (+*this).YUVtoRGB(); } //! In-place version of the previous function. CImg& YUVtoRGB() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::YUVtoRGB() : Input image dimension is dim=%u, " "should be a (Y,U,V) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const T Y = (*this)(x,y,z,0), U = (*this)(x,y,z,1), V = (*this)(x,y,z,2); (*this)(x,y,z,0) = (T)((Y + 1.140*V)*255.0f); (*this)(x,y,z,1) = (T)((Y - 0.395*U - 0.581*V)*255.0f); (*this)(x,y,z,2) = (T)((Y + 2.032*U)*255.0f); } } return *this; } //! Convert color pixels from (R,G,B) to (X,Y,Z)_709. CImg::type> get_RGBtoXYZ() const { typedef typename cimg::superset::type restype; return CImg(*this,false).RGBtoXYZ(); } //! In-place version of the previous function. CImg& RGBtoXYZ() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoXYZ() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const float R = (float)((*this)(x,y,z,0)/255.0f), G = (float)((*this)(x,y,z,1)/255.0f), B = (float)((*this)(x,y,z,2)/255.0f); (*this)(x,y,z,0) = (T)(0.412453*R + 0.357580*G + 0.180423*B); (*this)(x,y,z,1) = (T)(0.212671*R + 0.715160*G + 0.072169*B); (*this)(x,y,z,2) = (T)(0.019334*R + 0.119193*G + 0.950227*B); } } return *this; } //! Convert (X,Y,Z)_709 pixels of a color image into the (R,G,B) color space. CImg get_XYZtoRGB() const { return (+*this).XYZtoRGB(); } //! In-place version of the previous function. CImg& XYZtoRGB() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoRGB() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const float X = (float)(255.0f*(*this)(x,y,z,0)), Y = (float)(255.0f*(*this)(x,y,z,1)), Z = (float)(255.0f*(*this)(x,y,z,2)); (*this)(x,y,z,0) = (T)(3.240479*X - 1.537150*Y - 0.498535*Z); (*this)(x,y,z,1) = (T)(-0.969256*X + 1.875992*Y + 0.041556*Z); (*this)(x,y,z,2) = (T)(0.055648*X - 0.204043*Y + 1.057311*Z); } } return *this; } //! Convert (X,Y,Z)_709 pixels of a color image into the (L*,a*,b*) color space. CImg get_XYZtoLab() const { return (+*this).XYZtoLab(); } //! In-place version of the previous function. CImg& XYZtoLab() { #define cimg_Labf(x) ((x)>=0.008856?(std::pow(x,1/3.0)):(7.787*(x)+16.0/116.0)) if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoLab() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); const double Xn = 0.412453 + 0.357580 + 0.180423, Yn = 0.212671 + 0.715160 + 0.072169, Zn = 0.019334 + 0.119193 + 0.950227; cimg_forXYZ(*this,x,y,z) { const T X = (*this)(x,y,z,0), Y = (*this)(x,y,z,1), Z = (*this)(x,y,z,2); const double XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn, fX = cimg_Labf(XXn), fY = cimg_Labf(YYn), fZ = cimg_Labf(ZZn); (*this)(x,y,z,0) = (T)(116*fY-16); (*this)(x,y,z,1) = (T)(500*(fX-fY)); (*this)(x,y,z,2) = (T)(200*(fY-fZ)); } } return *this; } //! Convert (L,a,b) pixels of a color image into the (X,Y,Z) color space. CImg get_LabtoXYZ() const { return (+*this).LabtoXYZ(); } //! In-place version of the previous function. CImg& LabtoXYZ() { #define cimg_Labfi(x) ((x)>=0.206893?((x)*(x)*(x)):(((x)-16.0/116.0)/7.787)) if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::LabtoXYZ() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); const double Xn = 0.412453 + 0.357580 + 0.180423, Yn = 0.212671 + 0.715160 + 0.072169, Zn = 0.019334 + 0.119193 + 0.950227; cimg_forXYZ(*this,x,y,z) { const T L = (*this)(x,y,z,0), a = (*this)(x,y,z,1), b = (*this)(x,y,z,2); const double cY = (L+16)/116.0, Y = Yn*cimg_Labfi(cY), pY = std::pow(Y/Yn,1.0/3), cX = a/500+pY, X = Xn*cX*cX*cX, cZ = pY-b/200, Z = Zn*cZ*cZ*cZ; (*this)(x,y,z,0) = (T)(X); (*this)(x,y,z,1) = (T)(Y); (*this)(x,y,z,2) = (T)(Z); } } return *this; } //! Convert (X,Y,Z)_709 pixels of a color image into the (x,y,Y) color space. CImg get_XYZtoxyY() const { return (+*this).XYZtoxyY(); } //! In-place version of the previous function. CImg& XYZtoxyY() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::XYZtoxyY() : Input image dimension is dim=%u, " "should be a (X,Y,Z) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const T X = (*this)(x,y,z,0), Y = (*this)(x,y,z,1), Z = (*this)(x,y,z,2), sum = (X+Y+Z), nsum = sum>0?sum:1; (*this)(x,y,z,0) = X/nsum; (*this)(x,y,z,1) = Y/nsum; (*this)(x,y,z,2) = Y; } } return *this; } //! Convert (x,y,Y) pixels of a color image into the (X,Y,Z)_709 color space. CImg get_xyYtoXYZ() const { return (+*this).xyYtoXYZ(); } //! In-place version of the previous function. CImg& xyYtoXYZ() { if (!is_empty()) { if (dim!=3) throw CImgInstanceException("CImg<%s>::xyYtoXYZ() : Input image dimension is dim=%u, " "should be a (x,y,Y) image (dim=3)",pixel_type(),dim); cimg_forXYZ(*this,x,y,z) { const T px = (*this)(x,y,z,0), py = (*this)(x,y,z,1), Y = (*this)(x,y,z,2), ny = py>0?py:1; (*this)(x,y,z,0) = (T)(px*Y/ny); (*this)(x,y,z,1) = Y; (*this)(x,y,z,2) = (T)((1-px-py)*Y/ny); } } return *this; } //! Convert a (R,G,B) image to a (L,a,b) one. CImg get_RGBtoLab() const { return (+*this).RGBtoLab(); } //! In-place version of the previous function. CImg& RGBtoLab() { return RGBtoXYZ().XYZtoLab(); } //! Convert a (L,a,b) image to a (R,G,B) one. CImg get_LabtoRGB() const { return (+*this).LabtoRGB(); } //! In-place version of the previous function. CImg& LabtoRGB() { return LabtoXYZ().XYZtoRGB(); } //! Convert a (R,G,B) image to a (x,y,Y) one. CImg get_RGBtoxyY() const { return (+*this).RGBtoxyY(); } //! In-place version of the previous function. CImg& RGBtoxyY() { return RGBtoXYZ().XYZtoxyY(); } //! Convert a (x,y,Y) image to a (R,G,B) one. CImg get_xyYtoRGB() const { return (+*this).xyYtoRGB(); } //! In-place version of the previous function. CImg& xyYtoRGB() { return xyYtoXYZ().XYZtoRGB(); } //! Convert a (R,G,B) image to a Bayer-coded representation. /** \note First (upper-left) pixel if the red component of the pixel color. **/ CImg get_RGBtoBayer() const { if (is_empty()) return *this; if (dim!=3) throw CImgInstanceException("CImg<%s>::RGBtoBayer() : Input image dimension is dim=%u, " "should be a (R,G,B) image (dim=3)",pixel_type(),dim); CImg res(width,height,depth,1); const T *pR = ptr(0,0,0,0), *pG = ptr(0,0,0,1), *pB = ptr(0,0,0,2); T *ptrd = res.data; cimg_forXYZ(*this,x,y,z) { if (y%2) { if (x%2) *(ptrd++) = *pB; else *(ptrd++) = *pG; } else { if (x%2) *(ptrd++) = *pG; else *(ptrd++) = *pR; } ++pR; ++pG; ++pB; } return res; } // In-place version of the previous function. CImg& RGBtoBayer() { return get_RGBtoBayer().assign_to(*this); } //! Convert a Bayer-coded image to a (R,G,B) color image. CImg get_BayertoRGB(const unsigned int interpolation_type=3) const { if (is_empty()) return *this; if (dim!=1) throw CImgInstanceException("CImg<%s>::BayertoRGB() : Input image dimension is dim=%u, " "should be a Bayer image (dim=1)",pixel_type(),dim); CImg res(width,height,depth,3); CImg_3x3(I,T); T *pR = res.ptr(0,0,0,0), *pG = res.ptr(0,0,0,1), *pB = res.ptr(0,0,0,2); switch(interpolation_type) { case 3: // Edge-directed { CImg_3x3(R,T); CImg_3x3(G,T); CImg_3x3(B,T); cimg_forXYZ(*this,x,y,z) { const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x CImg& _draw_scanline(const int x0, const int x1, const int y, const tc *const color, const float opacity=1.0f, const float brightness=1.0f, const bool init=false) { static const T maxval = cimg::type::max(), minval = cimg::type::min(); static float nopacity = 0, copacity = 0; static unsigned int whz = 0; static const tc *col = 0; if (init) { nopacity = cimg::abs(opacity); copacity = 1.0f - cimg::max(opacity,0.0f); whz = width*height*depth; } else { const int nx0 = x0>0?x0:0, nx1 = x1=0) { col = color; const unsigned int off = whz-dx-1; T *ptrd = ptr(nx0,y); if (opacity>=1) // ** Opaque drawing ** { if (brightness==1.0f) // Brightness==1 { if (sizeof(T)!=1) cimg_forV(*this,k) { const T val = (T)*(col++); for (int x=dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const T val = (T)*(col++); std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } else { if (brightness>1.0f) // Brightness>1 { if (sizeof(T)!=1) cimg_forV(*this,k) { const float tval = (float)(*(col++)*brightness); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; for (int x=dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const float tval = (float)(*(col++)*brightness); const T val = tval<(float)maxval?(T)tval:maxval; std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } else // Brightness<1 { if (sizeof(T)!=1) cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); for (int x=dx; x>=0; --x) *(ptrd++) = val; ptrd+=off; } else cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); std::memset(ptrd,(int)val,dx+1); ptrd+=whz; } } } } else // ** Transparent drawing ** { if (brightness==1.0f) // Brightness==1 { cimg_forV(*this,k) { const T val = (T)*(col++); for (int x=dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } else { if (brightness>1.0f) // Brightness>1 { cimg_forV(*this,k) { const float tval = (float)(*(col++)*brightness); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; for (int x=dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } else // Brightness<=1 { cimg_forV(*this,k) { const T val = (T)(*(col++)*brightness); for (int x=dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; } ptrd+=off; } } } } } } return *this; } template CImg& _draw_scanline(const tc *const color, const float opacity=1.0f) { return _draw_scanline(0,0,0,color,opacity,1.0f,true); } //! Draw a colored point (pixel) in the instance image. /** \param x0 X-coordinate of the point. \param y0 Y-coordinate of the point. \param color Pointer to (or image of) \c dimv() consecutive values, defining the color channels. \param opacity Drawing opacity (optional). \note - Clipping is supported. - To set pixel values without clipping needs, you should use the faster CImg::operator()() function. \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; img.draw_point(50,50,color); \endcode \see CImg::operator()(). **/ template CImg& draw_point(const int x0, const int y0, const tc *const color, const float opacity=1.0f) { return draw_point(x0,y0,0,color,opacity); } template CImg& draw_point(const int x0, const int y0, const CImg& color, const float opacity=1.0f) { return draw_point(x0,y0,color.data,opacity); } //! Draw a colored point (pixel) in the instance image (for volumetric images). /** \note - Similar to CImg::draw_point() for 3D volumetric images. **/ template CImg& draw_point(const int x0, const int y0, const int z0, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_point() : Specified color is (null)",pixel_type()); if (x0>=0 && y0>=0 && z0>=0 && x0=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } } } return *this; } template CImg& draw_point(const int x0, const int y0, const int z0, const CImg& color, const float opacity=1.0f) { return draw_point(x0,y0,z0,color.data,opacity); } // Inner routine for drawing a cloud of points with generic type for coordinates. template CImg& _draw_point(const t& points, const tc *const color, const float opacity, const unsigned int W, const unsigned int H) { if (points && W && H>1) { if (H==2) for (unsigned int i=0; i img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; CImgList points; points.insert(CImg::vector(0,0)). .insert(CImg::vector(70,10)). .insert(CImg::vector(80,60)). .insert(CImg::vector(10,90)); img.draw_point(points,color); \endcode \sa CImg::draw_point(). **/ template CImg& draw_point(const CImgList& points, const tc *const color, const float opacity=1.0f) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_point(points,color,opacity,points.size,H); } template CImg& draw_point(const CImgList& points, const CImg& color, const float opacity=1.0f) { return draw_point(points,color.data,opacity); } //! Draw a cloud of points in the instance image. /** \note - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image (sequence of vectors aligned along the x-axis). **/ template CImg& draw_point(const CImg& points, const tc *const color, const float opacity=1.0f) { return _draw_point(points,color,opacity,points.width,points.height); } template CImg& draw_point(const CImg& points, const CImg& color, const float opacity=1.0f) { return draw_point(points,color.data,opacity); } //! Draw a colored line in the instance image. /** \param x0 X-coordinate of the starting line point. \param y0 Y-coordinate of the starting line point. \param x1 X-coordinate of the ending line point. \param y1 Y-coordinate of the ending line point. \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch Flag telling if a reinitialization of the hash state must be done (optional). \note - Clipping is supported. - Line routine uses Bresenham's algorithm. - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern. \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; img.draw_line(40,40,80,70,color); \endcode **/ template CImg& draw_line(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)",pixel_type()); static unsigned int hatch = ~0U-(~0U>>1); if (init_hatch) hatch = ~0U-(~0U>>1); const tc* col = color; const bool xdir = x0=dimx()) return *this; if (xleft<0) { yleft-=xleft*(yright-yleft)/(xright-xleft); xleft = 0; } if (xright>=dimx()) { yright-=(xright-dimx())*(yright-yleft)/(xright-xleft); xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { xup-=yup*(xdown-xup)/(ydown-yup); yup = 0; } if (ydown>=dimy()) { xdown-=(ydown-dimy())*(xdown-xup)/(ydown-yup); ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0,0,0); int dx = xright-xleft, dy = ydown-yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx0=1) { if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } col-=dim; } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=wh; } col-=dim; ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity=1-cimg::max(opacity,0.0f); if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { cimg_forV(*this,k) { const tc& c = *(col++); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } col-=dim; } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; cimg_forV(*this,k) { const tc& c = *(col++); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } col-=dim; ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } } return *this; } template CImg& draw_line(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(x0,y0,x1,y1,color.data,opacity,pattern,init_hatch); } //! Draw a colored line in the instance image (for volumetric images). /** \note - Similar to CImg::draw_line() for 3D volumetric images. **/ template CImg& draw_line(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_line() : Specified color is (null)",pixel_type()); static unsigned int hatch = ~0U-(~0U>>1); if (init_hatch) hatch = ~0U-(~0U>>1); const tc *col = color; int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1; if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (nx1<0 || nx0>=dimx()) return *this; if (nx0<0) { const int D=1+nx1-nx0; ny0-=nx0*(1+ny1-ny0)/D; nz0-=nx0*(1+nz1-nz0)/D; nx0 = 0; } if (nx1>=dimx()) { const int d=nx1-dimx(), D=1+nx1-nx0; ny1+=d*(1+ny0-ny1)/D; nz1+=d*(1+nz0-nz1)/D; nx1 = dimx()-1; } if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (ny1<0 || ny0>=dimy()) return *this; if (ny0<0) { const int D=1+ny1-ny0; nx0-=ny0*(1+nx1-nx0)/D; nz0-=ny0*(1+nz1-nz0)/D; ny0 = 0; } if (ny1>=dimy()) { const int d=ny1-dimy(), D=1+ny1-ny0; nx1+=d*(1+nx0-nx1)/D; nz1+=d*(1+nz0-nz1)/D; ny1 = dimy()-1; } if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1); if (nz1<0 || nz0>=dimz()) return *this; if (nz0<0) { const int D=1+nz1-nz0; nx0-=nz0*(1+nx1-nx0)/D; ny0-=nz0*(1+ny1-ny0)/D; nz0 = 0; } if (nz1>=dimz()) { const int d=nz1-dimz(), D=1+nz1-nz0; nx1+=d*(1+nx0-nx1)/D; ny1+=d*(1+ny0-ny1)/D; nz1 = dimz()-1; } const unsigned int dmax = cimg::max(cimg::abs(nx1-nx0),cimg::abs(ny1-ny0),nz1-nz0), whz = width*height*depth; const float px = (nx1-nx0)/(float)dmax, py = (ny1-ny0)/(float)dmax, pz = (nz1-nz0)/(float)dmax; float x = (float)nx0, y = (float)ny0, z = (float)nz0; if (opacity>=1) for (unsigned int t=0; t<=dmax; ++t) { if (!(~pattern) || (~pattern && pattern&hatch)) { T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z,0); cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } col-=dim; } x+=px; y+=py; z+=pz; if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); } else { const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); for (unsigned int t=0; t<=dmax; ++t) { if (!(~pattern) || (~pattern && pattern&hatch)) { T* ptrd = ptr((unsigned int)x,(unsigned int)y,(unsigned int)z,0); cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } col-=dim; } x+=px; y+=py; z+=pz; if (pattern) hatch=(hatch<<1)+(hatch>>(sizeof(unsigned int)*8-1)); } } } return *this; } template CImg& draw_line(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern,init_hatch); } //! Draw a textured line in the instance image. /** \param x0 X-coordinate of the starting line point. \param y0 Y-coordinate of the starting line point. \param x1 X-coordinate of the ending line point. \param y1 Y-coordinate of the ending line point. \param texture Texture image defining the pixel colors. \param tx0 X-coordinate of the starting texture point. \param ty0 Y-coordinate of the starting texture point. \param tx1 X-coordinate of the ending texture point. \param ty1 Y-coordinate of the ending texture point. \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch Flag telling if the hash variable must be reinitialized (optional). \note - Clipping is supported but not for texture coordinates. - Line routine uses the well known Bresenham's algorithm. \par Example: \code CImg img(100,100,1,3,0), texture("texture256x256.ppm"); const unsigned char color[] = { 255,128,64 }; img.draw_line(40,40,80,70,texture,0,0,255,255); \endcode **/ template CImg& draw_line(const int x0, const int y0, const int x1, const int y1, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!texture || texture.dim::draw_line() : specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static unsigned int hatch = ~0U-(~0U>>1); if (init_hatch) hatch = ~0U-(~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright-xleft; yleft-=xleft*(yright-yleft)/D; txleft-=xleft*(txright-txleft)/D; tyleft-=xleft*(tyright-tyleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright-dimx(), D = xright-xleft; yright-=d*(yright-yleft)/D; txright-=d*(txright-txleft)/D; tyright-=d*(tyright-tyleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown-yup; xup-=yup*(xdown-xup)/D; txup-=yup*(txdown-txup)/D; tyup-=yup*(tydown-tyup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown-dimy(), D = ydown-yup; xdown-=d*(xdown-xup)/D; txdown-=d*(txdown-txup)/D; tydown-=d*(tydown-tyup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0,0,0); int dx = xright-xleft, dy = ydown-yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = texture(tx,ty,0,k); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = texture(tx,ty,0,k); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity=1-cimg::max(opacity,0.0f); if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { const T c = (T)texture(tx,ty,0,k); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx; cimg_forV(*this,k) { const T c = (T)texture(tx,ty,0,k); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } } return *this; } //! Draw a textured line in the instance image, with perspective correction. template CImg& draw_line(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty() && z0>0 && z1>0) { if (!texture || texture.dim::draw_line() : specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static unsigned int hatch = ~0U-(~0U>>1); if (init_hatch) hatch = ~0U-(~0U>>1); const bool xdir = x0=dimx()) return *this; if (xleft<0) { const int D = xright-xleft; yleft-=xleft*(yright-yleft)/D; zleft-=xleft*(zright-zleft)/D; txleft-=xleft*(txright-txleft)/D; tyleft-=xleft*(tyright-tyleft)/D; xleft = 0; } if (xright>=dimx()) { const int d = xright-dimx(), D = xright-xleft; yright-=d*(yright-yleft)/D; zright-=d*(zright-zleft)/D; txright-=d*(txright-txleft)/D; tyright-=d*(tyright-tyleft)/D; xright = dimx()-1; } if (ydown<0 || yup>=dimy()) return *this; if (yup<0) { const int D = ydown-yup; xup-=yup*(xdown-xup)/D; zup-=yup*(zdown-zup)/D; txup-=yup*(txdown-txup)/D; tyup-=yup*(tydown-tyup)/D; yup = 0; } if (ydown>=dimy()) { const int d = ydown-dimy(), D = ydown-yup; xdown-=d*(xdown-xup)/D; zdown-=d*(zdown-zup)/D; txdown-=d*(txdown-txup)/D; tydown-=d*(tydown-tyup)/D; ydown = dimy()-1; } T *ptrd0 = ptr(nx0,ny0,0,0); int dx = xright-xleft, dy = ydown-yup; const bool steep = dy>dx; if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy); const int offx = (nx00?dx:1; if (opacity>=1) { if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; cimg_forV(*this,k) { *ptrd = texture((int)(tx/z),(int)(ty/z),0,k); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } else { const float nopacity = cimg::abs(opacity), copacity=1-cimg::max(opacity,0.0f); if (~pattern) for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; if (pattern&hatch) { const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; cimg_forV(*this,k) { const T c = (T)texture((int)(tx/z),(int)(ty/z),0,k); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } } hatch>>=1; if (!hatch) hatch = ~0U-(~0U>>1); ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } else for (int error=dx>>1, x=0; x<=dx; ++x) { T *ptrd = ptrd0; const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx; cimg_forV(*this,k) { const T c = (T)texture((int)(tx/z),(int)(ty/z),0,k); *ptrd = (T)(c*nopacity + *ptrd*copacity); ptrd+=wh; } ptrd0+=offx; if ((error-=dy)<0) { ptrd0+=offy; error+=dx; } } } } return *this; } // Inner routine for drawing set of consecutive lines with generic type for coordinates. template CImg& _draw_line(const t& points, const tc *const color, const float opacity, const unsigned int pattern, const bool init_hatch, const unsigned int W, const unsigned int H) { bool ninit_hatch = init_hatch; if (points && W>1 && H>1) { if (H==2) { const int x0 = (int)points(0,0), y0 = (int)points(0,1); int ox = x0, oy = y0; for (unsigned int i=1; i img(100,100,1,3,0); const unsigned char color[] = { 255,128,64 }; CImgList points; points.insert(CImg::vector(0,0)). .insert(CImg::vector(70,10)). .insert(CImg::vector(80,60)). .insert(CImg::vector(10,90)); img.draw_line(points,color); \endcode \sa CImg::draw_line(). **/ template CImg& draw_line(const CImgList& points, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_line(points,color,opacity,pattern,init_hatch,points.size,H); } template CImg& draw_line(const CImgList& points, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(points,color.data,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. /** \note - Similar to the previous function, where the N vertex coordinates are stored as a Nx2 or Nx3 image (sequence of vectors aligned along the x-axis). **/ template CImg& draw_line(const CImg& points, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_line(points,color,opacity,pattern,init_hatch,points.width,points.height); } template CImg& draw_line(const CImg& points, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_line(points,color.data,opacity,pattern,init_hatch); } // Inner routine for a drawing filled polygon with generic type for coordinates. template CImg& _draw_polygon(const t& points, const tc *const color, const float opacity, const unsigned int N) { if (!is_empty() && N>2) { if (!color) throw CImgArgumentException("CImg<%s>::draw_polygon() : Specified color is (null).",pixel_type()); _draw_scanline(color,opacity); int xmin = (int)(~0U>>1), xmax = 0, ymin = (int)(~0U>>1), ymax = 0; { for (unsigned int p=0; pxmax) xmax = x; if (yymax) ymax = y; } } if (xmax<0 || xmin>=dimx() || ymax<0 || ymin>=dimx()) return *this; const unsigned int nymin = ymin<0?0:(unsigned int)ymin, nymax = ymax>=dimy()?height-1:(unsigned int)ymax, dy = 1 + nymax - nymin; typedef typename cimg::last::type cint; CImg X(1+N,dy,1,2,0), done(N,2,1,1,0), tmp; int ox = (int)points(0,0), oy = (int)points(0,1), op = 0; for (unsigned int p=1; p<=N; ++p) { const int cp = p!=N?p:0, cx = (int)points(cp,0), cy = (int)points(cp,1), y0 = oy-nymin, y1 = cy-nymin, dir = (y1>y0?0:1); for (int x=ox, y=y0, _sx=1, _sy=1, _dx = cx>ox?cx-ox:((_sx=-1),ox-cx), _dy = y1>y0?y1-y0:((_sy=-1),y0-y1), _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy), _err = _dx>>1, _rx = _dy?(cx-ox)/_dy:0; _counter>0; --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0)) if (y>=0 && y<=(int)dy && y!=y0) X(++X(0,y,dir),y,dir) = x; if (!done(op,dir)) { X(++X(0,y0,dir),y0,dir) = ox; ++done(op,dir); } if (!done(cp,dir)) { X(++X(0,y1,dir),y1,dir) = cx; ++done(cp,dir); } ox = cx; oy = cy; op = cp; } for (int y=0; y<(int)dy; ++y) { tmp.assign(X.ptr(1,y,0),X(0,y,0),1,1,1,true).sort(); tmp.assign(X.ptr(1,y,1),X(0,y,1),1,1,1,true).sort(); for (int i=X(0,y,0); i>=1; --i) { const int xb = X(i,y,0), xe = X(i,y,1); if (xe>xb) _draw_scanline(xb,xe,nymin+y,color,opacity); else _draw_scanline(xe,xb,nymin+y,color,opacity); } } } return *this; } //! Draw a filled polygon in the instance image. template CImg& draw_polygon(const CImgList& points, const tc *const color, const float opacity=1.0f) { return _draw_polygon(points,color,opacity,points.size); } template CImg& draw_polygon(const CImgList& points, const CImg& color, const float opacity=1.0f) { return draw_polygon(points,color.data,opacity); } //! Draw a filled polygon in the instance image. template CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity=1.0f) { return _draw_polygon(points,color,opacity,points.width); } template CImg& draw_polygon(const CImg& points, const CImg& color, const float opacity=1.0f) { return draw_polygon(points,color.data,opacity); } // Inner routine for drawing an outlined polygon with generic point coordinates. template CImg& _draw_polygon(const t& points, const tc *const color, const float opacity, const unsigned int pattern, const unsigned int W, const unsigned int H) { bool ninit_hatch = true; if (points && W>2 && H>1) { if (H==2) { const int x0 = (int)points(0,0), y0 = (int)points(0,1); int ox = x0, oy = y0; for (unsigned int i=1; i CImg& draw_polygon(const CImgList& points, const tc *const color, const float opacity, const unsigned int pattern) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size())); return _draw_polygon(points,color,opacity,pattern,points.size,H); } template CImg& draw_polygon(const CImgList& points, const CImg& color, const float opacity, const unsigned int pattern) { return draw_polygon(points,color.data,opacity,pattern); } // Draw a polygon outline. template CImg& draw_polygon(const CImg& points, const tc *const color, const float opacity, const unsigned int pattern) { return _draw_polygon(points,color,opacity,pattern,points.width,points.height); } template CImg& draw_polygon(const CImg& points, const CImg& color, const float opacity, const unsigned int pattern) { return draw_polygon(points,color.data,opacity,pattern); } //! Draw a cubic spline curve in the instance image. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point \param u0 X-coordinate of the starting velocity \param v0 Y-coordinate of the starting velocity \param x1 X-coordinate of the ending curve point \param y1 Y-coordinate of the ending curve point \param u1 X-coordinate of the ending velocity \param v1 Y-coordinate of the ending velocity \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param precision Curve drawing precision (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch If \c true, init hatch motif. \note - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points and corresponding velocity vectors. - The spline is drawn as a serie of connected segments. The \p precision parameter sets the average number of pixels in each drawn segment. - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya), (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point and (\p xa,\p ya), (\p xb,\p yb) are two \e control points. The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from the control points as \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb). \par Example: \code CImg img(100,100,1,3,0); const unsigned char color[] = { 255,255,255 }; img.draw_spline(30,30,0,100,90,40,0,-100,color); \endcode **/ template CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, const int x1, const int y1, const float u1, const float v1, const tc *const color, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)",pixel_type()); bool ninit_hatch = init_hatch; const float dx = (float)(x1-x0), dy = (float)(y1-y0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1.0f + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0; for (float t=0.0f; t CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, const int x1, const int y1, const float u1, const float v1, const CImg& color, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,color.data,precision,opacity,pattern,init_hatch); } //! Draw a cubic spline curve in the instance image (for volumetric images). /** \note - Similar to CImg::draw_spline() for a 3D spline in a volumetric image. **/ template CImg& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, const tc *const color, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_spline() : Specified color is (null)",pixel_type()); bool ninit_hatch = init_hatch; const float dx = (float)(x1-x0), dy = (float)(y1-y0), dz = (float)(z1-z0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy),cimg::abs(dz)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, az = -2*dz + w0 + w1, bz = 3*dz - 2*w0 - w1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1.0f + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0, oz = z0; for (float t=0.0f; t CImg& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0, const int x1, const int y1, const int z1, const float u1, const float v1, const float w1, const CImg& color, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(x0,y0,z0,u0,v0,w0,x1,y1,z1,u1,v1,w1,color.data,precision,opacity,pattern,init_hatch); } //! Draw a cubic spline curve in the instance image. /** \param x0 X-coordinate of the starting curve point \param y0 Y-coordinate of the starting curve point \param u0 X-coordinate of the starting velocity \param v0 Y-coordinate of the starting velocity \param x1 X-coordinate of the ending curve point \param y1 Y-coordinate of the ending curve point \param u1 X-coordinate of the ending velocity \param v1 Y-coordinate of the ending velocity \param texture Texture image defining line pixel colors. \param tx0 X-coordinate of the starting texture point. \param ty0 Y-coordinate of the starting texture point. \param tx1 X-coordinate of the ending texture point. \param ty1 Y-coordinate of the ending texture point. \param precision Curve drawing precision (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \param init_hatch if \c true, reinit hatch motif. **/ template CImg& draw_spline(const int x0, const int y0, const float u0, const float v0, const int x1, const int y1, const float u1, const float v1, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { if (!is_empty()) { if (!texture || texture.dim::draw_line() : specified texture (%u,%u,%u,%u,%p) is not a valid argument.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); bool ninit_hatch = true; const float dx = (float)(x1-x0), dy = (float)(y1-y0), dmax = cimg::max(cimg::abs(dx),cimg::abs(dy)), ax = -2*dx + u0 + u1, bx = 3*dx - 2*u0 - u1, ay = -2*dy + v0 + v1, by = 3*dy - 2*v0 - v1, xprecision = dmax>0?precision/dmax:1.0f, tmax = 1.0f + (dmax>0?xprecision:0.0f); int ox = x0, oy = y0, otx = tx0, oty = ty0; for (float t1=0.0f; t1 CImg& _draw_spline(const tp& points, const tt& tangents, const tc *const color, const bool close_set, const float precision, const float opacity, const unsigned int pattern, const bool init_hatch, const unsigned int W, const unsigned int H) { bool ninit_hatch = init_hatch; if (points && tangents && W>1 && H>1) { if (H==2) { const int x0 = (int)points(0,0), y0 = (int)points(0,1); const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1); int ox = x0, oy = y0; float ou = u0, ov = v0; for (unsigned int i=1; i CImg& _draw_spline(const tp& points, const tc *const color, const bool close_set, const float precision, const float opacity, const unsigned int pattern, const bool init_hatch, const unsigned int W, const unsigned int H) { typedef typename cimg::superset::type ftype; CImg tangents; if (points && W>1 && H>1) { if (H==2) { tangents.assign(W,H); for (unsigned int p=0; p CImg& draw_spline(const CImgList& points, const CImgList& tangents, const tc *const color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) H = cimg::min(H,(unsigned int)(points[p].size()),(unsigned int)(tangents[p].size())); return _draw_spline(points,tangents,color,close_set,precision,opacity,pattern,init_hatch,points.size,H); } template CImg& draw_spline(const CImgList& points, const CImgList& tangents, const CImg& color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,tangents,color.data,close_set,precision,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImg& points, const CImg& tangents, const tc *const color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_spline(points,tangents,color,close_set,precision,opacity,pattern,init_hatch,points.width,points.height); } template CImg& draw_spline(const CImg& points, const CImg& tangents, const CImg& color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,tangents,color.data,close_set,precision,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored splines in the instance image. template CImg& draw_spline(const CImgList& points, const tc *const color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { unsigned int H = ~0U; cimglist_for(points,p) { const unsigned int s = points[p].size(); if (s CImg& draw_spline(const CImgList& points, CImg& color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,color.data,close_set,precision,opacity,pattern,init_hatch); } //! Draw a set of consecutive colored lines in the instance image. template CImg& draw_spline(const CImg& points, const tc *const color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return _draw_spline(points,color,close_set,precision,opacity,pattern,init_hatch,points.width,points.height); } template CImg& draw_spline(const CImg& points, const CImg& color, const bool close_set=false, const float precision=4.0f, const float opacity=1.0f, const unsigned int pattern=~0U, const bool init_hatch=true) { return draw_spline(points,color.data,close_set,precision,opacity,pattern,init_hatch); } //! Draw a colored arrow in the instance image. /** \param x0 X-coordinate of the starting arrow point (tail). \param y0 Y-coordinate of the starting arrow point (tail). \param x1 X-coordinate of the ending arrow point (head). \param y1 Y-coordinate of the ending arrow point (head). \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param angle Aperture angle of the arrow head (optional). \param length Length of the arrow head. If negative, describes a percentage of the arrow length (optional). \param opacity Drawing opacity (optional). \param pattern An integer whose bits describe the line pattern (optional). \note - Clipping is supported. **/ template CImg& draw_arrow(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float angle=30, const float length=-10, const float opacity=1.0f, const unsigned int pattern=~0U) { if (!is_empty()) { const float u = (float)(x0-x1), v = (float)(y0-y1), sq = u*u+v*v, deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f, l = (length>=0)?length:-length*(float)std::sqrt(sq)/100; if (sq>0) { const double cl = std::cos(ang-deg), sl = std::sin(ang-deg), cr = std::cos(ang+deg), sr = std::sin(ang+deg); const int xl = x1+(int)(l*cl), yl = y1+(int)(l*sl), xr = x1+(int)(l*cr), yr = y1+(int)(l*sr), xc = x1+(int)((l+1)*(cl+cr))/2, yc = y1+(int)((l+1)*(sl+sr))/2; draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity); } else draw_point(x0,y0,color,opacity); } return *this; } template CImg& draw_arrow(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float angle=30, const float length=-10, const float opacity=1.0f, const unsigned int pattern=~0U) { return draw_arrow(x0,y0,x1,y1,color.data,angle,length,opacity,pattern); } //! Draw a sprite image in the instance image. /** \param sprite Sprite image. \param x0 X-coordinate of the sprite position. \param y0 Y-coordinate of the sprite position. \param z0 Z-coordinate of the sprite position. \param v0 V-coordinate of the sprite position. \param opacity Drawing opacity (optional). \note - Clipping is supported. **/ template CImg& draw_image(const CImg& sprite, const int x0, const int y0=0, const int z0=0, const int v0=0, const float opacity=1.0f) { if (!is_empty()) { if (!sprite) throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); const int lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); const t *ptrs = sprite.data - (bx?x0:0) - (by?y0*sprite.dimx():0) - (bz?z0*sprite.dimx()*sprite.dimy():0) - (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); const unsigned int offX = width-lX, soffX = sprite.width-lX, offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); if (lX>0 && lY>0 && lZ>0 && lV>0) for (int v=0; v=1) for (int x=0; x::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); if (this==&sprite) return draw_image(CImg(sprite),x0,y0,z0,v0,opacity); const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); const int lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); const T *ptrs = sprite.data - (bx?x0:0) - (by?y0*sprite.dimx():0) - (bz?z0*sprite.dimx()*sprite.dimy():0) - (bv?v0*sprite.dimx()*sprite.dimy()*sprite.dimz():0); const unsigned int offX = width-lX, soffX = sprite.width-lX, offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ), slX = lX*sizeof(T); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); if (lX>0 && lY>0 && lZ>0 && lV>0) for (int v=0; v=1) for (int y=0; y CImg& draw_image(const CImg& sprite, const CImg& mask, const int x0, const int y0=0, const int z0=0, const int v0=0, const float mask_valmax=1.0f, const float opacity=1.0f) { if (!is_empty()) { if (!sprite) throw CImgArgumentException("CImg<%s>::draw_image() : Specified sprite image (%u,%u,%u,%u,%p) is empty.", pixel_type(),sprite.width,sprite.height,sprite.depth,sprite.dim,sprite.data); if (!mask) throw CImgArgumentException("CImg<%s>::draw_image() : Specified mask image (%u,%u,%u,%u,%p) is empty.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); if ((void*)this==(void*)&sprite) return draw_image(CImg(sprite),mask,x0,y0,z0,v0); if(mask.width!=sprite.width || mask.height!=sprite.height || mask.depth!=sprite.depth) throw CImgArgumentException("CImg<%s>::draw_image() : Mask dimension is (%u,%u,%u,%u), while sprite is (%u,%u,%u,%u)", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,sprite.width,sprite.height,sprite.depth,sprite.dim); const bool bx=(x0<0), by=(y0<0), bz=(z0<0), bv=(v0<0); const int lX = sprite.dimx() - (x0+sprite.dimx()>dimx()?x0+sprite.dimx()-dimx():0) + (bx?x0:0), lY = sprite.dimy() - (y0+sprite.dimy()>dimy()?y0+sprite.dimy()-dimy():0) + (by?y0:0), lZ = sprite.dimz() - (z0+sprite.dimz()>dimz()?z0+sprite.dimz()-dimz():0) + (bz?z0:0), lV = sprite.dimv() - (v0+sprite.dimv()>dimv()?v0+sprite.dimv()-dimv():0) + (bv?v0:0); const int coff = -(bx?x0:0)-(by?y0*mask.dimx():0)-(bz?z0*mask.dimx()*mask.dimy():0)-(bv?v0*mask.dimx()*mask.dimy()*mask.dimz():0), ssize = mask.dimx()*mask.dimy()*mask.dimz(); const ti *ptrs = sprite.data + coff; const tm *ptrm = mask.data + coff; const unsigned int offX = width-lX, soffX = sprite.width-lX, offY = width*(height-lY), soffY = sprite.width*(sprite.height-lY), offZ = width*height*(depth-lZ), soffZ = sprite.width*sprite.height*(sprite.depth-lZ); T *ptrd = ptr(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,v0<0?0:v0); if (lX>0 && lY>0 && lZ>0 && lV>0) for (int v=0; v=dimx()?dimx()-1-nx1:0) + (nx0<0?nx0:0), lY = (1+ny1-ny0) + (ny1>=dimy()?dimy()-1-ny1:0) + (ny0<0?ny0:0), lZ = (1+nz1-nz0) + (nz1>=dimz()?dimz()-1-nz1:0) + (nz0<0?nz0:0), lV = (1+nv1-nv0) + (nv1>=dimv()?dimv()-1-nv1:0) + (nv0<0?nv0:0); const unsigned int offX = width-lX, offY = width*(height-lY), offZ = width*height*(depth-lZ); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); T *ptrd = ptr(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nv0<0?0:nv0); if (lX>0 && lY>0 && lZ>0 && lV>0) for (int v=0; v=1) { if (sizeof(T)!=1) { for (int x=0; x CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity=1.0f) { if (!color) throw CImgArgumentException("CImg<%s>::draw_rectangle : specified color is (null)",pixel_type()); cimg_forV(*this,k) draw_rectangle(x0,y0,z0,k,x1,y1,z1,k,color[k],opacity); return *this; } template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity=1.0f) { return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity); } //! Draw a 3D outlined colored rectangle in the instance image. template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const tc *const color, const float opacity, const unsigned int pattern) { return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true). draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false). draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false). draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false). draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true). draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false). draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false). draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false). draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true). draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true). draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true). draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true); } template CImg& draw_rectangle(const int x0, const int y0, const int z0, const int x1, const int y1, const int z1, const CImg& color, const float opacity, const unsigned int pattern) { return draw_rectangle(x0,y0,z0,x1,y1,z1,color.data,opacity,pattern); } //! Draw a 2D filled colored rectangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1). /** \param x0 X-coordinate of the upper-left rectangle corner. \param y0 Y-coordinate of the upper-left rectangle corner. \param x1 X-coordinate of the lower-right rectangle corner. \param y1 Y-coordinate of the lower-right rectangle corner. \param color Pointer to \c dimv() consecutive values of type \c T, defining the drawing color. \param opacity Drawing opacity (optional). \note - Clipping is supported. **/ template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity=1.0f) { return draw_rectangle(x0,y0,0,x1,y1,depth-1,color,opacity); } template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity=1.0f) { return draw_rectangle(x0,y0,x1,y1,color.data,opacity); } //! Draw a 2D outlined colored rectangle. template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const tc *const color, const float opacity, const unsigned int pattern) { return draw_line(x0,y0,x1,y0,color,opacity,pattern,true). draw_line(x1,y0,x1,y1,color,opacity,pattern,false). draw_line(x1,y1,x0,y1,color,opacity,pattern,false). draw_line(x0,y1,x0,y0,color,opacity,pattern,false); } template CImg& draw_rectangle(const int x0, const int y0, const int x1, const int y1, const CImg& color, const float opacity, const unsigned int pattern) { return draw_rectangle(x0,y0,x1,y1,color.data,opacity,pattern); } // Inner macro for drawing triangles. #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ _sxn=1, \ _sxr=1, \ _sxl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, \ _errr = _dyr/2, \ _errl = _dyl/2, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ _sxn=1, _scn=1, \ _sxr=1, _scr=1, \ _sxl=1, _scl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errcn = _errn, \ _errr = _dyr/2, _errcr = _errr, \ _errl = _dyl/2, _errcl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rcn = _dyn?(c2-c1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rcr = _dyr?(c2-c0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ _sxn=1, _stxn=1, _styn=1, \ _sxr=1, _stxr=1, _styr=1, \ _sxl=1, _stxl=1, _styl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \ _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \ _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ _sxn=1, _scn=1, _stxn=1, _styn=1, \ _sxr=1, _scr=1, _stxr=1, _styr=1, \ _sxl=1, _scl=1, _stxl=1, _styl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \ _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \ _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \ _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dyn = y2-y1, \ _dyr = y2-y0, \ _dyl = y1-y0, \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \ _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \ _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \ _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \ _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rcn = _dyn?(c2-c1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rcr = _dyr?(c2-c0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \ txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \ _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \ for (int y = y0<0?0:y0, \ xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \ txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \ tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \ lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \ lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \ xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \ txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \ tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \ lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \ lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \ _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \ _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \ _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \ _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \ _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \ _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \ _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \ _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \ _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \ _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \ _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \ _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \ _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \ _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \ _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \ _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \ _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \ _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \ _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \ _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \ _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \ _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \ _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \ _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \ _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \ _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \ _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \ _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \ _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \ _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \ _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \ _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \ _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \ cimg::min((int)(img).height-y-1,y2-y)), \ _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \ _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \ _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \ _rxn = _dyn?(x2-x1)/_dyn:0, \ _rtxn = _dyn?(tx2-tx1)/_dyn:0, \ _rtyn = _dyn?(ty2-ty1)/_dyn:0, \ _rlxn = _dyn?(lx2-lx1)/_dyn:0, \ _rlyn = _dyn?(ly2-ly1)/_dyn:0, \ _rxr = _dyr?(x2-x0)/_dyr:0, \ _rtxr = _dyr?(tx2-tx0)/_dyr:0, \ _rtyr = _dyr?(ty2-ty0)/_dyr:0, \ _rlxr = _dyr?(lx2-lx0)/_dyr:0, \ _rlyr = _dyr?(ly2-ly0)/_dyr:0, \ _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \ (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \ _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \ _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \ (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \ _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \ (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \ _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \ (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \ _counter>=0; --_counter, ++y, \ xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \ txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \ tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \ lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \ lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \ xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \ tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \ lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \ lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \ _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \ (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \ _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \ _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \ _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \ _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl)) // Draw a colored triangle (inner routine, uses bresenham's algorithm) template CImg& _draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float opacity, const float brightness) { _draw_scanline(color,opacity); int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2); if (ny0=0) { if ((nx1-nx0)*(ny2-ny0)-(nx2-nx0)*(ny1-ny0)<0) _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,brightness); else _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,brightness); } return *this; } //! Draw a 2D filled colored triangle in the instance image. template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).",pixel_type()); _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1.0f); } return *this; } template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float opacity=1.0f) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity); } //! Draw a 2D outlined colored triangle template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float opacity, const unsigned int pattern) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).",pixel_type()); draw_line(x0,y0,x1,y1,color,opacity,pattern,true). draw_line(x1,y1,x2,y2,color,opacity,pattern,false). draw_line(x2,y2,x0,y0,color,opacity,pattern,false); } return *this; } template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float opacity, const unsigned int pattern) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opacity,pattern); } //! Draw a 2D Gouraud-filled triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param color = array of dimv() values of type \c T, defining the global drawing color. \param c0 = brightness of the first corner. \param c1 = brightness of the second corner. \param c2 = brightness of the third corner. \param opacity = opacity of the drawing. \note Clipping is supported. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const float c0, const float c1, const float c2, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).",pixel_type()); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)(c0*256), nc1 = (int)(c1*256), nc2 = (int)(c2*256); if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2); if (ny0=0) { _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; if (xrightcleft?cright-cleft:cleft-cright, rc = dx?(cright-cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc-(dx?dx*(dc/dx):0); int errc = dx>>1; if (xleft<0 && dx) cleft-=xleft*(cright-cleft)/dx; if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const tc *col = color; cimg_forV(*this,k) { const float tval = (float)((cleft**(col++))/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x=xleft; x<=xright; ++x) { const tc *col = color; cimg_forV(*this,k) { const float tval = (float)((cleft**(col++))/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } } } } return *this; } template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const float c0, const float c1, const float c2, const float opacity=1.0f) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,c0,c1,c2,opacity); } //! Draw a 2D textured triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param opacity = opacity of the drawing. \param brightness = brightness of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float opacity=1.0f, const float brightness=1.0f) { if (!is_empty()) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2); if (ny0=0) { _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y, nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) { int xleft = xleft0, xright = xright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrighttxleft?txright-txleft:txleft-txright, dty = tyright>tyleft?tyright-tyleft:tyleft-tyright, rtx = dx?(txright-txleft)/dx:0, rty = dx?(tyright-tyleft)/dx:0, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndtx = dtx-(dx?dx*(dtx/dx):0), ndty = dty-(dx?dx*(dty/dx):0); int errtx = dx>>1, errty = errtx; if (xleft<0 && dx) { txleft-=xleft*(txright-txleft)/dx; tyleft-=xleft*(tyright-tyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) { if (brightness<=1) for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(brightness**col); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(brightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } else { if (brightness<=1) for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { *ptrd = (T)(nopacity*brightness**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(brightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } } } } return *this; } //! Draw a textured triangle with perspective correction. template CImg& draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float opacity=1.0f, const float brightness=1.0f) { if (!is_empty() && z0>0 && z1>0 && z2>0) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1.0f/z0, nz1 = 1.0f/z1, nz2 = 1.0f/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2); if (ny0=0) { float ptxl = (ntx1-ntx0)/(ny1-ny0), ptxr = (ntx2-ntx0)/(ny2-ny0), ptxn = (ntx2-ntx1)/(ny2-ny1), ptyl = (nty1-nty0)/(ny1-ny0), ptyr = (nty2-nty0)/(ny2-ny0), ptyn = (nty2-nty1)/(ny2-ny1), pzl = (nz1-nz0)/(ny1-ny0), pzr = (nz2-nz0)/(ny2-ny0), pzn = (nz2-nz1)/(ny2-ny1), zr = ny0>=0?nz0:(nz0-ny0*(nz2-nz0)/(ny2-ny0)), txr = ny0>=0?ntx0:(ntx0-ny0*(ntx2-ntx0)/(ny2-ny0)), tyr = ny0>=0?nty0:(nty0-ny0*(nty2-nty0)/(ny2-ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0-ny0*(nz1-nz0)/(ny1-ny0))):(pzl=pzn,(nz1-ny1*(nz2-nz1)/(ny2-ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0-ny0*(ntx1-ntx0)/(ny1-ny0))):(ptxl=ptxn,(ntx1-ny1*(ntx2-ntx1)/(ny2-ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0-ny0*(nty1-nty0)/(ny1-ny0))):(ptyl=ptyn,(nty1-ny1*(nty2-nty1)/(ny2-ny1))); _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xright=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) { if (brightness<=1) for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(brightness**col); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(brightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } else { if (brightness<=1) for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { *ptrd = (T)(nopacity*brightness**col + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } else for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(brightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; } } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } } } return *this; } //! Draw a 2D phong-shaded triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param color = array of dimv() values of type \c T, defining the global drawing color. \param light = light image. \param lx0 = X-coordinate of the first corner in the light image. \param ly0 = Y-coordinate of the first corner in the light image. \param lx1 = X-coordinate of the second corner in the light image. \param ly1 = Y-coordinate of the second corner in the light image. \param lx2 = X-coordinate of the third corner in the light image. \param ly2 = Y-coordinate of the third corner in the light image. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const tc *const color, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_triangle : Specified color is (null).",pixel_type()); if (!light) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified light texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),light.width,light.height,light.depth,light.dim,light.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; const int whz = width*height*depth, offx = dim*whz-1; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2); if (ny0=0) { _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; if (xrightlxleft?lxright-lxleft:lxleft-lxright, dly = lyright>lyleft?lyright-lyleft:lyleft-lyright, rlx = dx?(lxright-lxleft)/dx:0, rly = dx?(lyright-lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx-(dx?dx*(dlx/dx):0), ndly = dly-(dx?dx*(dly/dx):0); int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { lxleft-=xleft*(lxright-lxleft)/dx; lyleft-=xleft*(lyright-lyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const t lightness = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { const float tval = (float)(*(col++)*lightness); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x=xleft; x<=xright; ++x) { const t lightness = light(lxleft,lyleft); const tc *col = color; cimg_forV(*this,k) { const float tval = (float)(*(col++)*lightness); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } } } } return *this; } template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& color, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1.0f) { return draw_triangle(x0,y0,x1,y1,x2,y2,color.data,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity); } //! Draw a 2D textured triangle with Gouraud-Shading in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param c0 = brightness value of the first corner. \param c1 = brightness value of the second corner. \param c2 = brightness value of the third corner. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float c0, const float c1, const float c2, const float opacity=1) { if (!is_empty()) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, nc0 = (int)(c0*256), nc1 = (int)(c1*256), nc2 = (int)(c2*256); if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2); if (ny0=0) { _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y, nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) { int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrightcleft?cright-cleft:cleft-cright, dtx = txright>txleft?txright-txleft:txleft-txright, dty = tyright>tyleft?tyright-tyleft:tyleft-tyright, rc = dx?(cright-cleft)/dx:0, rtx = dx?(txright-txleft)/dx:0, rty = dx?(tyright-tyleft)/dx:0, sc = cright>cleft?1:-1, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndc = dc-(dx?dx*(dc/dx):0), ndtx = dtx-(dx?dx*(dtx/dx):0), ndty = dty-(dx?dx*(dty/dx):0); int errc = dx>>1, errtx = errc, errty = errc; if (xleft<0 && dx) { cleft-=xleft*(cright-cleft)/dx; txleft-=xleft*(txright-txleft)/dx; tyleft-=xleft*(tyright-tyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(cleft**col/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)val; ptrd+=whz; col+=twhz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x=xleft; x<=xright; ++x) { const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(cleft**col/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } } } return *this; } //! Draw a gouraud + textured triangle with perspective correction. template CImg& draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const float c0, const float c1, const float c2, const float opacity=1.0f) { if (!is_empty() && z0>0 && z1>0 && z2>0) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nc0 = (int)(c0*256), nc1 = (int)(c1*256), nc2 = (int)(c2*256); float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1.0f/z0, nz1 = 1.0f/z1, nz2 = 1.0f/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2); if (ny0=0) { float ptxl = (ntx1-ntx0)/(ny1-ny0), ptxr = (ntx2-ntx0)/(ny2-ny0), ptxn = (ntx2-ntx1)/(ny2-ny1), ptyl = (nty1-nty0)/(ny1-ny0), ptyr = (nty2-nty0)/(ny2-ny0), ptyn = (nty2-nty1)/(ny2-ny1), pzl = (nz1-nz0)/(ny1-ny0), pzr = (nz2-nz0)/(ny2-ny0), pzn = (nz2-nz1)/(ny2-ny1), zr = ny0>=0?nz0:(nz0-ny0*(nz2-nz0)/(ny2-ny0)), txr = ny0>=0?ntx0:(ntx0-ny0*(ntx2-ntx0)/(ny2-ny0)), tyr = ny0>=0?nty0:(nty0-ny0*(nty2-nty0)/(ny2-ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0-ny0*(nz1-nz0)/(ny1-ny0))):(pzl=pzn,(nz1-ny1*(nz2-nz1)/(ny2-ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0-ny0*(ntx1-ntx0)/(ny1-ny0))):(ptxl=ptxn,(ntx1-ny1*(ntx2-ntx1)/(ny2-ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0-ny0*(nty1-nty0)/(ny1-ny0))):(ptyl=ptyn,(nty1-ny1*(nty2-nty1)/(ny2-ny1))); _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightcleft?cright-cleft:cleft-cright, rc = dx?(cright-cleft)/dx:0, sc = cright>cleft?1:-1, ndc = dc-(dx?dx*(dc/dx):0); const float pentez = (zright-zleft)/dx, pentetx = (txright-txleft)/dx, pentety = (tyright-tyleft)/dx; int errc = dx>>1; if (xleft<0 && dx) { cleft-=xleft*(cright-cleft)/dx; zleft-=xleft*(zright-zleft)/dx; txleft-=xleft*(txright-txleft)/dx; tyleft-=xleft*(tyright-tyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(cleft**col/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } else for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(cleft**col/256); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } } } return *this; } //! Draw a phong-shaded 2D textured triangle in the instance image, at coordinates (\c x0,\c y0)-(\c x1,\c y1)-(\c x2,\c y2). /** \param x0 = X-coordinate of the first corner in the instance image. \param y0 = Y-coordinate of the first corner in the instance image. \param x1 = X-coordinate of the second corner in the instance image. \param y1 = Y-coordinate of the second corner in the instance image. \param x2 = X-coordinate of the third corner in the instance image. \param y2 = Y-coordinate of the third corner in the instance image. \param texture = texture image used to fill the triangle. \param tx0 = X-coordinate of the first corner in the texture image. \param ty0 = Y-coordinate of the first corner in the texture image. \param tx1 = X-coordinate of the second corner in the texture image. \param ty1 = Y-coordinate of the second corner in the texture image. \param tx2 = X-coordinate of the third corner in the texture image. \param ty2 = Y-coordinate of the third corner in the texture image. \param light = light image. \param lx0 = X-coordinate of the first corner in the light image. \param ly0 = Y-coordinate of the first corner in the light image. \param lx1 = X-coordinate of the second corner in the light image. \param ly1 = Y-coordinate of the second corner in the light image. \param lx2 = X-coordinate of the third corner in the light image. \param ly2 = Y-coordinate of the third corner in the light image. \param opacity = opacity of the drawing. \note Clipping is supported, but texture coordinates do not support clipping. **/ template CImg& draw_triangle(const int x0, const int y0, const int x1, const int y1, const int x2, const int y2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1.0f) { if (!is_empty()) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2); if (ny0=0) { _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y, nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) { int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0, txleft = txleft0, txright = txright0, tyleft = tyleft0, tyright = tyright0; if (xrightlxleft?lxright-lxleft:lxleft-lxright, dly = lyright>lyleft?lyright-lyleft:lyleft-lyright, dtx = txright>txleft?txright-txleft:txleft-txright, dty = tyright>tyleft?tyright-tyleft:tyleft-tyright, rlx = dx?(lxright-lxleft)/dx:0, rly = dx?(lyright-lyleft)/dx:0, rtx = dx?(txright-txleft)/dx:0, rty = dx?(tyright-tyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, stx = txright>txleft?1:-1, sty = tyright>tyleft?1:-1, ndlx = dlx-(dx?dx*(dlx/dx):0), ndly = dly-(dx?dx*(dly/dx):0), ndtx = dtx-(dx?dx*(dtx/dx):0), ndty = dty-(dx?dx*(dty/dx):0); int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx; if (xleft<0 && dx) { lxleft-=xleft*(lxright-lxleft)/dx; lyleft-=xleft*(lyright-lyleft)/dx; txleft-=xleft*(txright-txleft)/dx; tyleft-=xleft*(tyright-tyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const tl lightness = light(lxleft,lyleft); const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(lightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)val; ptrd+=whz; col+=twhz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } else for (int x=xleft; x<=xright; ++x) { const tl lightness = light(lxleft,lyleft); const t *col = texture.ptr(txleft,tyleft); cimg_forV(*this,k) { const float tval = (float)(lightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0); tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0); } } } } return *this; } //! Draw a phong + textured triangle with perspective correction. template CImg& draw_triangle(const int x0, const int y0, const float z0, const int x1, const int y1, const float z1, const int x2, const int y2, const float z2, const CImg& texture, const int tx0, const int ty0, const int tx1, const int ty1, const int tx2, const int ty2, const CImg& light, const int lx0, const int ly0, const int lx1, const int ly1, const int lx2, const int ly2, const float opacity=1.0f) { if (!is_empty() && z0>0 && z1>0 && z2>0) { if (!texture) throw CImgArgumentException("CImg<%s>::draw_triangle() : Specified texture (%u,%u,%u,%u,%p) is empty.", pixel_type(),texture.width,texture.height,texture.depth,texture.dim,texture.data); static const T minval = cimg::type::min(), maxval = cimg::type::max(); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const int whz = width*height*depth, twhz = texture.width*texture.height*texture.depth, offx = dim*whz-1; int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2, nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2; float ntx0 = tx0/z0, nty0 = ty0/z0, ntx1 = tx1/z1, nty1 = ty1/z1, ntx2 = tx2/z2, nty2 = ty2/z2, nz0 = 1.0f/z0, nz1 = 1.0f/z1, nz2 = 1.0f/z2; if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1); if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2); if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2); if (ny0=0) { float ptxl = (ntx1-ntx0)/(ny1-ny0), ptxr = (ntx2-ntx0)/(ny2-ny0), ptxn = (ntx2-ntx1)/(ny2-ny1), ptyl = (nty1-nty0)/(ny1-ny0), ptyr = (nty2-nty0)/(ny2-ny0), ptyn = (nty2-nty1)/(ny2-ny1), pzl = (nz1-nz0)/(ny1-ny0), pzr = (nz2-nz0)/(ny2-ny0), pzn = (nz2-nz1)/(ny2-ny1), zr = ny0>=0?nz0:(nz0-ny0*(nz2-nz0)/(ny2-ny0)), txr = ny0>=0?ntx0:(ntx0-ny0*(ntx2-ntx0)/(ny2-ny0)), tyr = ny0>=0?nty0:(nty0-ny0*(nty2-nty0)/(ny2-ny0)), zl = ny1>=0?(ny0>=0?nz0:(nz0-ny0*(nz1-nz0)/(ny1-ny0))):(pzl=pzn,(nz1-ny1*(nz2-nz1)/(ny2-ny1))), txl = ny1>=0?(ny0>=0?ntx0:(ntx0-ny0*(ntx1-ntx0)/(ny1-ny0))):(ptxl=ptxn,(ntx1-ny1*(ntx2-ntx1)/(ny2-ny1))), tyl = ny1>=0?(ny0>=0?nty0:(nty0-ny0*(nty1-nty0)/(ny1-ny0))):(ptyl=ptyn,(nty1-ny1*(nty2-nty1)/(ny2-ny1))); _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y, nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) { if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; } int xleft = xleft0, xright = xright0, lxleft = lxleft0, lxright = lxright0, lyleft = lyleft0, lyright = lyright0; float zleft = zl, zright = zr, txleft = txl, txright = txr, tyleft = tyl, tyright = tyr; if (xrightlxleft?lxright-lxleft:lxleft-lxright, dly = lyright>lyleft?lyright-lyleft:lyleft-lyright, rlx = dx?(lxright-lxleft)/dx:0, rly = dx?(lyright-lyleft)/dx:0, slx = lxright>lxleft?1:-1, sly = lyright>lyleft?1:-1, ndlx = dlx-(dx?dx*(dlx/dx):0), ndly = dly-(dx?dx*(dly/dx):0); const float pentez = (zright-zleft)/dx, pentetx = (txright-txleft)/dx, pentety = (tyright-tyleft)/dx; int errlx = dx>>1, errly = errlx; if (xleft<0 && dx) { zleft-=xleft*(zright-zleft)/dx; lxleft-=xleft*(lxright-lxleft)/dx; lyleft-=xleft*(lyright-lyleft)/dx; txleft-=xleft*(txright-txleft)/dx; tyleft-=xleft*(tyright-tyleft)/dx; } if (xleft<0) xleft=0; if (xright>=dimx()-1) xright=dimx()-1; T* ptrd = ptr(xleft,y,0,0); if (opacity>=1) for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const tl lightness = light(lxleft,lyleft); const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(lightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = val; ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } else for (int x=xleft; x<=xright; ++x) { const float invz = 1.0f/zleft; const tl lightness = light(lxleft,lyleft); const t *col = texture.ptr((int)(txleft*invz),(int)(tyleft*invz)); cimg_forV(*this,k) { const float tval = (float)(lightness**col); const T val = tval<(float)maxval?(tval>(float)minval?(T)tval:minval):maxval; *ptrd = (T)(nopacity*val + *ptrd*copacity); ptrd+=whz; col+=twhz; } ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety; lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0); lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0); } zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl; } } } return *this; } // Draw an ellipse on the instance image (inner routine). template CImg& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity, const unsigned int pattern) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_ellipse : Specified color is (null).",pixel_type()); _draw_scanline(color,opacity); const float nr1 = cimg::abs(r1), nr2 = cimg::abs(r2), norm = (float)std::sqrt(ru*ru+rv*rv), u = norm>0?ru/norm:1, v = norm>0?rv/norm:0, rmax = cimg::max(nr1,nr2), l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2), l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2), a = l1*u*u + l2*v*v, b = u*v*(l1-l2), c = l1*v*v + l2*u*u; const int yb = (int)std::sqrt(a*rmax*rmax/(a*c-b*b)), tymin = y0-yb, tymax = y0+yb, ymin = tymin<0?0:tymin, ymax = tymax>=dimy()?height-1:tymax; int oxmin = 0, oxmax = 0; bool first_line = true; for (int y=ymin; y<=ymax; ++y) { const float Y = y-y0 + (y0?(float)std::sqrt(delta)/a:0.0f, bY = b*Y/a, fxmin = x0-0.5f-bY-sdelta, fxmax = x0+0.5f-bY+sdelta; const int xmin = (int)fxmin, xmax = (int)fxmax; if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity); else { if (first_line) { if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity); else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity); first_line = false; } else { if (xmin CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity, const unsigned int pattern) { if (pattern) _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,pattern); return *this; } template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const CImg& color, const float opacity, const unsigned int pattern) { return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity,pattern); } //! Draw a filled ellipse. /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param r1 = First radius of the ellipse. \param r2 = Second radius of the ellipse. \param ru = X-coordinate of the orientation vector related to the first radius. \param rv = Y-coordinate of the orientation vector related to the first radius. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const tc *const color, const float opacity=1.0f) { return _draw_ellipse(x0,y0,r1,r2,ru,rv,color,opacity,0U); } template CImg& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float ru, const float rv, const CImg& color, const float opacity=1.0f) { return draw_ellipse(x0,y0,r1,r2,ru,rv,color.data,opacity); } //! Draw a filled ellipse on the instance image /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param tensor = Diffusion tensor describing the ellipse. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const tc *const color, const float opacity=1.0f) { CImgList eig = tensor.get_symmetric_eigen(); const CImg &val = eig[0], &vec = eig[1]; return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity); } template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const CImg& color, const float opacity=1.0f) { return draw_ellipse(x0,y0,tensor,color.data,opacity); } //! Draw an outlined ellipse on the instance image /** \param x0 = X-coordinate of the ellipse center. \param y0 = Y-coordinate of the ellipse center. \param tensor = Diffusion tensor describing the ellipse. \param color = array of dimv() values of type \c T, defining the drawing color. \param pattern = If zero, the ellipse is filled, else pattern is an integer whose bits describe the outline pattern. \param opacity = opacity of the drawing. **/ template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const tc *const color, const float opacity, const unsigned int pattern) { CImgList eig = tensor.get_symmetric_eigen(); const CImg &val = eig[0], &vec = eig[1]; return draw_ellipse(x0,y0,val(0),val(1),vec(0,0),vec(0,1),color,opacity,pattern); } template CImg& draw_ellipse(const int x0, const int y0, const CImg &tensor, const CImg& color, const float opacity, const unsigned int pattern) { return draw_ellipse(x0,y0,tensor,color.data,opacity,pattern); } //! Draw a filled circle on the instance image /** \param x0 X-coordinate of the circle center. \param y0 Y-coordinate of the circle center. \param radius Circle radius. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. \note - Circle version of the Bresenham's algorithm is used. **/ template CImg& draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).",pixel_type()); _draw_scanline(color,opacity); if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; if (y0>=0 && y0=0) { const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y; if (y1>=0 && y1=0 && y2=0 && y1=0 && y2 CImg& draw_circle(const int x0, const int y0, int radius, const CImg& color, const float opacity=1.0f) { return draw_circle(x0,y0,radius,color.data,opacity); } //! Draw an outlined circle. /** \param x0 X-coordinate of the circle center. \param y0 Y-coordinate of the circle center. \param radius Circle radius. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. **/ template CImg& draw_circle(const int x0, const int y0, int radius, const tc *const color, const float opacity, const unsigned int) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_circle : Specified color is (null).",pixel_type()); if (radius<0 || x0-radius>=dimx() || y0+radius<0 || y0-radius>=dimy()) return *this; if (!radius) return draw_point(x0,y0,color,opacity); draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity). draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity); if (radius==1) return *this; for (int f=1-radius, ddFx=0, ddFy=-(radius<<1), x=0, y=radius; x=0) { f+=(ddFy+=2); --y; } ++x; ++(f+=(ddFx+=2)); if (x!=y+1) { const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y; draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity). draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity); if (x!=y) draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity). draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity); } } } return *this; } template CImg& draw_circle(const int x0, const int y0, int radius, const CImg& color, const float opacity, const unsigned int foo) { return draw_circle(x0,y0,radius,color.data,opacity,foo); } //! Draw a text into the instance image. /** \param text = a C-string containing the text to display. \param x0 = X-coordinate of the text in the instance image. \param y0 = Y-coordinate of the text in the instance image. \param fgcolor = an array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param bgcolor = an array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font = List of font characters used for the drawing. \param opacity = opacity of the drawing. \note Clipping is supported. \see get_font(). **/ template CImg& draw_text(const char *const text, const int x0, const int y0, const T *const fgcolor, const T *const bgcolor, const CImgList& font, const float opacity=1.0f) { if (!text) throw CImgArgumentException("CImg<%s>::draw_text() : Specified input string is (null).",pixel_type()); if (!font) throw CImgArgumentException("CImg<%s>::draw_text() : Specified font (%u,%p) is empty.", pixel_type(),font.size,font.data); if (is_empty()) { // If needed, pre-compute needed size of the image int x = 0, y = 0, w = 0; for (int i=0; iw) w = x; x = 0; break; case '\t': x+=4*font[' '].width; break; default: if (cw) w=x; y+=font[' '].height; } assign(x0+w,y0+y,1,font[' '].dim,0); if (bgcolor) cimg_forV(*this,k) get_shared_channel(k).fill((T)bgcolor[k]); } int x = x0, y = y0; CImg letter; for (int i=0; i=512) draw_image(letter,mask,x,y,0,0,(T)1,opacity); else draw_image(letter,x,y,0,0,opacity); x+=letter.width; } break; } } return *this; } template CImg& draw_text(const char *const text, const int x0, const int y0, const CImg& fgcolor, const CImg& bgcolor, const CImgList& font, const float opacity=1.0f) { const CImg fgcol(fgcolor), bgcol(bgcolor); return draw_text(text,x0,y0,fgcol.data,bgcol.data,font,opacity); } //! Draw a text into the instance image. /** \param text = a C-string containing the text to display. \param x0 = X-coordinate of the text in the instance image. \param y0 = Y-coordinate of the text in the instance image. \param fgcolor = an array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param bgcolor = an array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font_size = Height of the desired font (11,13,24,38 or 57) \param opacity = opacity of the drawing. \note Clipping is supported. \see get_font(). **/ CImg& draw_text(const char *const text, const int x0, const int y0, const T *const fgcolor, const T *const bgcolor=0, const unsigned int font_size=11, const float opacity=1.0f) { return draw_text(text,x0,y0,fgcolor,bgcolor,CImgList::get_font(font_size),opacity); } template CImg& draw_text(const char *const text, const int x0, const int y0, const CImg& fgcolor, const CImg& bgcolor, const unsigned int font_size=11, const float opacity=1.0f) { const CImg fgcol(fgcolor), bgcol(bgcolor); return draw_text(text,x0,y0,fgcol.data,bgcol.data,font_size,opacity); } //! Draw a text into the instance image. /** \param x0 X-coordinate of the text in the instance image. \param y0 Y-coordinate of the text in the instance image. \param fgcolor Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param bgcolor Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font_size Size of the font (nearest match). \param opacity Drawing opacity. \param format 'printf'-style format string, followed by arguments. \note Clipping is supported. **/ CImg& draw_text(const int x0, const int y0, const T *const fgcolor, const T *const bgcolor, const unsigned int font_size, const float opacity, const char *format,...) { char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,format); std::vsprintf(tmp,format,ap); va_end(ap); return draw_text(tmp,x0,y0,fgcolor,bgcolor,font_size,opacity); } template CImg& draw_text(const int x0, const int y0, const CImg& fgcolor, const CImg& bgcolor, const unsigned int font_size, const float opacity, const char *format,...) { char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,format); std::vsprintf(tmp,format,ap); va_end(ap); return draw_text(tmp,x0,y0,fgcolor,bgcolor,font_size,opacity); } //! Draw a text into the instance image. /** \param x0 X-coordinate of the text in the instance image. \param y0 Y-coordinate of the text in the instance image. \param fgcolor Array of dimv() values of type \c T, defining the foreground color (0 means 'transparent'). \param bgcolor Array of dimv() values of type \c T, defining the background color (0 means 'transparent'). \param font Font used for drawing text. \param opacity Drawing opacity. \param format 'printf'-style format string, followed by arguments. \note Clipping is supported. **/ template CImg& draw_text(const int x0, const int y0, const T *const fgcolor, const T *const bgcolor, const CImgList& font, const float opacity, const char *format,...) { char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,format); std::vsprintf(tmp,format,ap); va_end(ap); return draw_text(tmp,x0,y0,fgcolor,bgcolor,font,opacity); } template CImg& draw_text(const int x0, const int y0, const CImg& fgcolor, const CImg& bgcolor, const CImgList& font, const float opacity, const char *format,...) { char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,format); std::vsprintf(tmp,format,ap); va_end(ap); return draw_text(tmp,x0,y0,fgcolor,bgcolor,font,opacity); } //! Draw a vector field in the instance image, using a colormap. /** \param flow Image of 2d vectors used as input data. \param color Image of dimv()-D vectors corresponding to the color of each arrow. \param sampling Length (in pixels) between each arrow. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). \param opacity Opacity of the drawing. \param pattern Used pattern to draw lines. \note Clipping is supported. **/ template CImg& draw_quiver(const CImg& flow, const t2 *const color, const unsigned int sampling=25, const float factor=-20, const int quiver_type=0, const float opacity=1.0f, const unsigned int pattern=~0U) { return draw_quiver(flow,CImg(color,dim,1,1,1,true),sampling,factor,quiver_type,opacity,pattern); } //! Draw a vector field in the instance image, using a colormap. /** \param flow Image of 2d vectors used as input data. \param color Image of dimv()-D vectors corresponding to the color of each arrow. \param sampling Length (in pixels) between each arrow. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length). \param quiver_type Type of plot. Can be 0 (arrows) or 1 (segments). \param opacity Opacity of the drawing. \param pattern Used pattern to draw lines. \note Clipping is supported. **/ template CImg& draw_quiver(const CImg& flow, const CImg& color, const unsigned int sampling=25, const float factor=-20, const int quiver_type=0, const float opacity=1.0f, const unsigned int pattern=~0U) { if (!is_empty()) { if (!flow || flow.dim!=2) throw CImgArgumentException("CImg<%s>::draw_quiver() : Specified flow (%u,%u,%u,%u,%p) has wrong dimensions.", pixel_type(),flow.width,flow.height,flow.depth,flow.dim,flow.data); if (sampling<=0) throw CImgArgumentException("CImg<%s>::draw_quiver() : Incorrect sampling value = %g",pixel_type(),sampling); const bool colorfield = (color.width==flow.width && color.height==flow.height && color.depth==1 && color.dim==dim); float vmax,fact; if (factor<=0) { float m, M = (float)flow.get_norm_pointwise(2).maxmin(m); vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); fact = -factor; } else { fact = factor; vmax = 1; } for (unsigned int y=sampling/2; y CImg& draw_graph(const CImg& data, const tc *const color, const unsigned int gtype=1, const double ymin=0, const double ymax=0, const float opacity=1.0f, const unsigned int pattern=~0U) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_graph() : Specified color is (null)",pixel_type()); tc *color1 = new tc[dim], *color2 = new tc[dim]; cimg_forV(*this,k) { color1[k]=(tc)(color[k]*0.6f); color2[k]=(tc)(color[k]*0.3f); } float m = (float)ymin, M = (float)ymax; if (ymin==ymax) m = (float)data.maxmin(M); if (m==M) { --m; ++M; } const float ca = height>1?(float)(M-m)/(height-1):0; const int Y0 = (int)(-m/ca); int pY = 0; bool init_hatch = true; if (gtype<3) cimg_foroff(data,off) { const int Y = (int)((data[off]-m)/ca); switch (gtype) { case 0: // plot with points { const unsigned int X = off*width/data.size(); draw_point(X,Y,color,opacity); } break; case 1: // plot with segments if (off>0) { draw_line((int)((off-1)*width/data.size()),pY,(int)(off*width/data.size()),Y,color,opacity,pattern,init_hatch); init_hatch = false; } break; case 2: // plot with bars { const unsigned int X = off*width/data.size(), nX = (off+1)*width/data.size()-1; draw_rectangle(X,(int)Y0,nX,Y,color1,opacity); draw_line(X,Y,X,(int)Y0,color2,opacity); draw_line(X,(int)Y0,nX,(int)Y0,Y<=Y0?color2:color,opacity); draw_line(nX,Y,nX,(int)Y0,color,opacity); draw_line(X,Y,nX,Y,Y<=Y0?color:color2,opacity); } break; } pY = Y; } else // plot with cubic interpolation { const CImg ndata = data.get_shared_points(0,data.size()-1); cimg_forX(*this,x) { const int Y = (int)((ndata.cubic_pix1d((float)x*ndata.width/width)-m)/ca); if (x>0) draw_line(x,pY,x+1,Y,color,opacity,pattern,init_hatch); init_hatch = false; pY = Y; } } delete[] color1; delete[] color2; } return *this; } template CImg& draw_graph(const CImg& data, const CImg& color, const unsigned int gtype=1, const double ymin=0, const double ymax=0, const float opacity=1.0f, const unsigned int pattern=~0U) { return draw_graph(data,color.data,gtype,ymin,ymax,opacity,pattern); } //! Draw a labeled horizontal axis on the instance image. /** \param xvalues Lower bound of the x-range. \param y Y-coordinate of the horizontal axis in the instance image. \param color Array of dimv() values of type \c T, defining the drawing color. \param opacity Drawing opacity. \param pattern Drawing pattern. \note if \c precision==0, precision of the labels is automatically computed. \see draw_graph(). **/ template CImg& draw_axis(const CImg& xvalues, const int y, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U) { if (!is_empty()) { int siz = (int)xvalues.size()-1; if (siz<=0) draw_line(0,y,width-1,y,color,opacity,pattern); else { if (xvalues[0] CImg& draw_axis(const CImg& xvalues, const int y, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U) { return draw_axis(xvalues,y,color.data,opacity,pattern); } //! Draw a labeled vertical axis on the instance image. template CImg& draw_axis(const int x, const CImg& yvalues, const tc *const color, const float opacity=1.0f, const unsigned int pattern=~0U) { if (!is_empty()) { int siz = (int)yvalues.size()-1; if (siz<=0) draw_line(x,0,x,height-1,color,opacity,pattern); else { if (yvalues[0]=dimy()-11?dimy()-11:tmp), xt = x-(int)std::strlen(txt)*7; draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity); if (xt>0) draw_text(txt,xt,nyi,color,(tc*)0,11,opacity); else draw_text(txt,x+3,nyi,color,(tc*)0,11,opacity); } } } return *this; } template CImg& draw_axis(const int x, const CImg& yvalues, const CImg& color, const float opacity=1.0f, const unsigned int pattern=~0U) { return draw_axis(x,yvalues,color.data,opacity,pattern); } //! Draw a labeled horizontal+vertical axis on the instance image. template CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, const tc *const color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { if (!is_empty()) { const CImg nxvalues(xvalues.data,xvalues.size(),1,1,1,true); const int sizx = (int)xvalues.size()-1, wm1 = (int)(width)-1; if (sizx>0) { float ox = (float)nxvalues[0]; for (unsigned int x=1; x nyvalues(yvalues.data,yvalues.size(),1,1,1,true); const int sizy = (int)yvalues.size()-1, hm1 = (int)(height)-1; if (sizy>0) { float oy = (float)nyvalues[0]; for (unsigned int y=1; y CImg& draw_axis(const CImg& xvalues, const CImg& yvalues, const CImg& color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_axis(xvalues,yvalues,color.data,opacity,patternx,patterny); } //! Draw a labeled horizontal+vertical axis on the instance image. template CImg& draw_axis(const float x0, const float x1, const float y0, const float y1, const tc *const color, const int subdivisionx=-60, const int subdivisiony=-60, const float precisionx=0, const float precisiony=0, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { const float dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0); const float px = (precisionx==0)?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx, py = (precisiony==0)?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony; return draw_axis(CImg::sequence(subdivisionx>0?subdivisionx:1-dimx()/subdivisionx,x0,x1).round(px), CImg::sequence(subdivisiony>0?subdivisiony:1-dimy()/subdivisiony,y0,y1).round(py), color,opacity,patternx,patterny); } template CImg& draw_axis(const float x0, const float x1, const float y0, const float y1, const CImg& color, const int subdivisionx=-60, const int subdivisiony=-60, const float precisionx=0, const float precisiony=0, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_axis(x0,x1,y0,y1,color.data,subdivisionx,subdivisiony,precisionx,precisiony,opacity,patternx,patterny); } //! Draw grid on the instance image template CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, const tc *const color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { if (!is_empty()) { if (xvalues) cimg_foroff(xvalues,x) { const int xi = (int)xvalues[x]; if (xi>=0 && xi=0 && yi CImg& draw_grid(const CImg& xvalues, const CImg& yvalues, const CImg& color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_grid(xvalues,yvalues,color.data,opacity,patternx,patterny); } //! Draw grid on the instance image template CImg& draw_grid(const float deltax, const float deltay, const float offsetx, const float offsety, const bool invertx, const bool inverty, const tc *const color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { CImg seqx, seqy; if (deltax!=0) { const float dx = deltax>0?deltax:width*-deltax/100; const unsigned int nx = (unsigned int)(width/dx); seqx = CImg::sequence(1+nx,0,(unsigned int)(dx*nx)); if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)width); if (invertx) cimg_foroff(seqx,x) seqx(x) = width-1-seqx(x); } if (deltay!=0) { const float dy = deltay>0?deltay:height*-deltay/100; const unsigned int ny = (unsigned int)(height/dy); seqy = CImg::sequence(1+ny,0,(unsigned int)(dy*ny)); if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)height); if (inverty) cimg_foroff(seqy,y) seqy(y) = height-1-seqy(y); } return draw_grid(seqx,seqy,color,opacity,patternx,patterny); } template CImg& draw_grid(const float deltax, const float deltay, const float offsetx, const float offsety, const bool invertx, const bool inverty, const CImg& color, const float opacity=1.0f, const unsigned int patternx=~0U, const unsigned int patterny=~0U) { return draw_grid(deltax,deltay,offsetx,offsety,invertx,inverty,color.data,opacity,patternx,patterny); } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. /** \param x X-coordinate of the starting point of the region to fill. \param y Y-coordinate of the starting point of the region to fill. \param z Z-coordinate of the starting point of the region to fill. \param color An array of dimv() values of type \c T, defining the drawing color. \param region Image that will contain the mask of the filled region mask, as an output. \param sigma Tolerance concerning neighborhood values. \param opacity Opacity of the drawing. \param high_connexity Tells if 8-connexity must be used (only for 2D images). \return \p region is initialized with the binary mask of the filled region. **/ template CImg& draw_fill(const int x, const int y, const int z, const tc *const color, CImg& region, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \ res = true; \ const T *reference_col = reference_color.ptr() + dim, *ptrs = ptr(x,y,z) + siz; \ for (unsigned int i = dim; res && i; --i) { ptrs-=whz; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \ region(x,y,z) = (t)(res?1:noregion); \ } #define _cimg_draw_fill_set(x,y,z) { \ const tc *col = color; \ T *ptrd = ptr(x,y,z); \ if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)*(col++); ptrd+=whz; } \ else cimg_forV(*this,k) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whz; } \ } #define _cimg_draw_fill_insert(x,y,z) { \ if (posr1>=remaining.height) remaining.resize(3,remaining.height<<1,1,1,0); \ unsigned int *ptrr = remaining.ptr(0,posr1); \ *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \ } #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \ const unsigned int tx = x, ty = y, tz = z; \ _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \ } if (!color) throw CImgArgumentException("CImg<%s>::draw_fill() : Specified color is (null).",pixel_type()); region.assign(width,height,depth,1,(t)0); if (x>=0 || x=0 || y=0 || z::type itype; const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const unsigned int whz = width*height*depth, siz = dim*whz, W1 = width-1, H1 = height-1, D1 = depth-1; const bool threed = depth>1; const CImg reference_color = get_vector_at(x,y,z); CImg remaining(3,512,1,1,0); remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z; unsigned int posr0 = 0, posr1 = 1; region(x,y,z) = (t)1; const t noregion = ((t)1==(t)2)?(t)0:(t)(-1); if (threed) do // 3D version of the filling algorithm { const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++); if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } bool cont, res; unsigned int nxc = xc; do // X-backward { _cimg_draw_fill_set(nxc,yc,zc); _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0); _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,ycposr0); else do // 2D version of the filling algorithm { const unsigned int *pcurr = remaining.ptr(0,posr0++), xc = *(pcurr++), yc = *(pcurr++); if (posr0>=512) { remaining.translate(0,posr0); posr1-=posr0; posr0 = 0; } bool cont, res; unsigned int nxc = xc; do // X-backward { _cimg_draw_fill_set(nxc,yc,0); _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0); _cimg_draw_fill_test_neighbor(nxc,yc+1,0,ycposr0); if (noregion) cimg_for(region,ptr,t) if (*ptr==noregion) *ptr = (t)0; } return *this; } template CImg& draw_fill(const int x, const int y, const int z, const CImg& color, CImg& region, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { return draw_fill(x,y,z,color.data,region,sigma,opacity,high_connexity); } //! Draw a 3D filled region starting from a point (\c x,\c y,\ z) in the instance image. /** \param x = X-coordinate of the starting point of the region to fill. \param y = Y-coordinate of the starting point of the region to fill. \param z = Z-coordinate of the starting point of the region to fill. \param color = an array of dimv() values of type \c T, defining the drawing color. \param sigma = tolerance concerning neighborhood values. \param opacity = opacity of the drawing. **/ template CImg& draw_fill(const int x, const int y, const int z, const tc *const color, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { CImg tmp; return draw_fill(x,y,z,color,tmp,sigma,opacity,high_connexity); } template CImg& draw_fill(const int x, const int y, const int z, const CImg& color, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { return draw_fill(x,y,z,color.data,sigma,opacity,high_connexity); } //! Draw a 2D filled region starting from a point (\c x,\c y) in the instance image. /** \param x = X-coordinate of the starting point of the region to fill. \param y = Y-coordinate of the starting point of the region to fill. \param color = an array of dimv() values of type \c T, defining the drawing color. \param sigma = tolerance concerning neighborhood values. \param opacity = opacity of the drawing. **/ template CImg& draw_fill(const int x, const int y, const tc *const color, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { CImg tmp; return draw_fill(x,y,0,color,tmp,sigma,opacity,high_connexity); } template CImg& draw_fill(const int x, const int y, const CImg& color, const float sigma=0, const float opacity=1.0f, const bool high_connexity=false) { return draw_fill(x,y,color.data,sigma,opacity,high_connexity); } //! Draw a plasma square in the instance image. /** \param x0 = X-coordinate of the upper-left corner of the plasma. \param y0 = Y-coordinate of the upper-left corner of the plasma. \param x1 = X-coordinate of the lower-right corner of the plasma. \param y1 = Y-coordinate of the lower-right corner of the plasma. \param alpha = Alpha-parameter of the plasma. \param beta = Beta-parameter of the plasma. \param opacity = opacity of the drawing. **/ CImg& draw_plasma(const int x0, const int y0, const int x1, const int y1, const double alpha=1.0, const double beta=1.0, const float opacity=1.0f) { if (!is_empty()) { typedef typename cimg::superset::type ftype; const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); int nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1; if (nx1=dimx()) nx1 = width-1; if (ny0<0) ny0 = 0; if (ny1>=dimy()) ny1 = height-1; const int xc = (nx0+nx1)/2, yc = (ny0+ny1)/2, dx = (xc-nx0), dy = (yc-ny0); const ftype dc = (ftype)(std::sqrt((double)(dx*dx+dy*dy))*alpha + beta); ftype val = 0; cimg_forV(*this,k) { if (opacity>=1) { const ftype val0 = (*this)(nx0,ny0,0,k), val1 = (*this)(nx1,ny0,0,k), val2 = (*this)(nx0,ny1,0,k), val3 = (*this)(nx1,ny1,0,k); (*this)(xc,ny0,0,k) = (T)((val0+val1)/2); (*this)(xc,ny1,0,k) = (T)((val2+val3)/2); (*this)(nx0,yc,0,k) = (T)((val0+val2)/2); (*this)(nx1,yc,0,k) = (T)((val1+val3)/2); do { val = (ftype)(0.25f*((ftype)((*this)(nx0,ny0,0,k)) + (ftype)((*this)(nx1,ny0,0,k)) + (ftype)((*this)(nx1,ny1,0,k)) + (ftype)((*this)(nx0,ny1,0,k))) + dc*cimg::grand()); } while (val<(ftype)cimg::type::min() || val>(ftype)cimg::type::max()); (*this)(xc,yc,0,k) = (T)val; } else { const ftype val0 = (*this)(nx0,ny0,0,k), val1 = (*this)(nx1,ny0,0,k), val2 = (*this)(nx0,ny1,0,k), val3 = (*this)(nx1,ny1,0,k); (*this)(xc,ny0,0,k) = (T)(((val0+val1)*nopacity + copacity*(*this)(xc,ny0,0,k))/2); (*this)(xc,ny1,0,k) = (T)(((val2+val3)*nopacity + copacity*(*this)(xc,ny1,0,k))/2); (*this)(nx0,yc,0,k) = (T)(((val0+val2)*nopacity + copacity*(*this)(nx0,yc,0,k))/2); (*this)(nx1,yc,0,k) = (T)(((val1+val3)*nopacity + copacity*(*this)(nx1,yc,0,k))/2); do { val = (ftype)(0.25f*(((ftype)((*this)(nx0,ny0,0,k)) + (ftype)((*this)(nx1,ny0,0,k)) + (ftype)((*this)(nx1,ny1,0,k)) + (ftype)((*this)(nx0,ny1,0,k))) + dc*cimg::grand())*nopacity + copacity*(*this)(xc,yc,0,k)); } while (val<(ftype)cimg::type::min() || val>(ftype)cimg::type::max()); (*this)(xc,yc,0,k) = (T)val; } } if (xc!=nx0 || yc!=ny0) { draw_plasma(nx0,ny0,xc,yc,alpha,beta,opacity); draw_plasma(xc,ny0,nx1,yc,alpha,beta,opacity); draw_plasma(nx0,yc,xc,ny1,alpha,beta,opacity); draw_plasma(xc,yc,nx1,ny1,alpha,beta,opacity); } } return *this; } //! Draw a plasma in the instance image. /** \param alpha = Alpha-parameter of the plasma. \param beta = Beta-parameter of the plasma. \param opacity = opacity of the drawing. **/ CImg& draw_plasma(const double alpha=1.0, const double beta=1.0, const float opacity=1.0f) { return draw_plasma(0,0,width-1,height-1,alpha,beta,opacity); } //! Draw a 1D gaussian function in the instance image. /** \param xc = X-coordinate of the gaussian center. \param sigma = Standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const double sigma, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)",pixel_type()); const double sigma2 = 2*sigma*sigma; const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const unsigned int whz = width*height*depth; const tc *col = color; cimg_forX(*this,x) { const float dx = (x-xc); const double val = std::exp( -dx*dx/sigma2 ); T *ptrd = ptr(x,0,0,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; } } return *this; } template CImg& draw_gaussian(const float xc, const double sigma, const CImg& color, const float opacity=1.0f) { return draw_gaussian(xc,sigma,color.data,opacity); } //! Draw an anisotropic 2D gaussian function in the instance image. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param tensor = 2x2 covariance matrix. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (tensor.width!=2 || tensor.height!=2 || tensor.depth!=1 || tensor.dim!=1) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 2x2 matrix.", pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); if (!color) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Specified color is (null)",pixel_type()); const CImg invT = tensor.get_inverse(), invT2 = (invT*invT)/(-2.0); const t &a=invT2(0,0), &b=2*invT2(1,0), &c=invT2(1,1); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const unsigned int whz = width*height*depth; const tc *col = color; float dy = -yc; cimg_forY(*this,y) { float dx = -xc; cimg_forX(*this,x) { const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy); T *ptrd = ptr(x,y,0,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; ++dx; } ++dy; } } return *this; } template CImg& draw_gaussian(const float xc, const float yc, const CImg& tensor, const CImg& color, const float opacity=1.0f) { return draw_gaussian(xc,yc,tensor,color.data,opacity); } //! Draw an isotropic 2D gaussian function in the instance image /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param sigma = standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float sigma, const tc *const color, const float opacity=1.0f) { return draw_gaussian(xc,yc,CImg::diagonal(sigma,sigma),color,opacity); } template CImg& draw_gaussian(const float xc, const float yc, const float sigma, const CImg& color, const float opacity=1.0f) { return draw_gaussian(xc,yc,sigma,color.data,opacity); } //! Draw an anisotropic 3D gaussian function in the instance image. /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param zc = Z-coordinate of the gaussian center. \param tensor = 3x3 covariance matrix. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, const tc *const color, const float opacity=1.0f) { if (!is_empty()) { if (tensor.width!=3 || tensor.height!=3 || tensor.depth!=1 || tensor.dim!=1) throw CImgArgumentException("CImg<%s>::draw_gaussian() : Tensor parameter (%u,%u,%u,%u,%p) is not a 3x3 matrix.", pixel_type(),tensor.width,tensor.height,tensor.depth,tensor.dim,tensor.data); const CImg invT = tensor.get_inverse(), invT2 = (invT*invT)/(-2.0); const t a=invT(0,0), b=2*invT(1,0), c=2*invT(2,0), d=invT(1,1), e=2*invT(2,1), f=invT(2,2); const float nopacity = cimg::abs(opacity), copacity = 1-cimg::max(opacity,0.0f); const unsigned int whz = width*height*depth; const tc *col = color; cimg_forXYZ(*this,x,y,z) { const float dx = (x-xc), dy = (y-yc), dz = (z-zc); const double val = std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz); T *ptrd = ptr(x,y,z,0); if (opacity>=1) cimg_forV(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whz; } else cimg_forV(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whz; } col-=dim; } } return *this; } template CImg& draw_gaussian(const float xc, const float yc, const float zc, const CImg& tensor, const CImg& color, const float opacity=1.0f) { return draw_gaussian(xc,yc,zc,tensor,color.data,opacity); } //! Draw an isotropic 3D gaussian function in the instance image /** \param xc = X-coordinate of the gaussian center. \param yc = Y-coordinate of the gaussian center. \param zc = Z-coordinate of the gaussian center. \param sigma = standard variation of the gaussian distribution. \param color = array of dimv() values of type \c T, defining the drawing color. \param opacity = opacity of the drawing. **/ template CImg& draw_gaussian(const float xc, const float yc, const float zc, const double sigma, const tc *const color, const float opacity=1.0f) { return draw_gaussian(xc,yc,zc,CImg::diagonal(sigma,sigma,sigma),color,opacity); } template CImg& draw_gaussian(const float xc, const float yc, const float zc, const double sigma, const CImg& color, const float opacity=1.0f) { return draw_gaussian(xc,yc,zc,sigma,color.data,opacity); } //! Draw a 3D object in the instance image /** \param X = X-coordinate of the 3d object position \param Y = Y-coordinate of the 3d object position \param Z = Z-coordinate of the 3d object position \param points = Image N*3 describing 3D point coordinates \param primitives = List of P primitives \param colors = List of P color (or textures) \param opacities = Image of P opacities \param render_type = Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud) \param double_sided = Tell if object faces have two sides or are oriented. \param focale = length of the focale \param lightx = X-coordinate of the light \param lighty = Y-coordinate of the light \param lightz = Z-coordinate of the light \param ambient_light = Brightness of the ambient light **/ template CImg& draw_object3d(const float X, const float Y, const float Z, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float ambient_light=0.05f) { static CImg light_texture; if (is_empty() || !points || !primitives) return *this; if (!colors || !opacities) throw CImgArgumentException("CImg<%s>::draw_object3d() : Undefined colors or opacities",pixel_type()); if (points.height<3) return draw_object3d(X,Y,Z,points.get_resize(-100,3,1,1,0),primitives,colors,opacities, render_type,double_sided,focale,lightx,lighty,lightz,ambient_light); // Create light texture for phong-like rendering if (render_type==5) { if (colors.size>primitives.size) light_texture.assign(colors[primitives.size])/=255; else { static float olightx = 0, olighty = 0, olightz = 0, oambient_light = 0; if (!light_texture || lightx!=olightx || lighty!=olighty || lightz!=olightz || ambient_light!=oambient_light) { light_texture.assign(512,512); const float white[] = { 1.0f }, dlx = lightx-X, dly = lighty-Y, dlz = lightz-Z, nl = (float)std::sqrt(dlx*dlx+dly*dly+dlz*dlz), nlx = light_texture.width/2*(1+dlx/nl), nly = light_texture.height/2*(1+dly/nl); (light_texture.draw_gaussian(nlx,nly,light_texture.width/3.0f,white)+=ambient_light); olightx = lightx; olighty = lighty; olightz = lightz; oambient_light = ambient_light; } } } // Compute 3D to 2D projection CImg projections(points.width,2); cimg_forX(points,l) { const float x = (float)points(l,0), y = (float)points(l,1), z = (float)points(l,2); const float projectedz = z + Z + focale; projections(l,1) = Y + focale*y/projectedz; projections(l,0) = X + focale*x/projectedz; } // Compute and sort visible primitives CImg visibles(primitives.size); CImg zrange(primitives.size); unsigned int nb_visibles = 0; const float zmin = -focale+1.5f; { cimglist_for(primitives,l) { const CImg& primitive = primitives[l]; switch (primitive.size()) { case 1: // Point { const unsigned int i0 = (unsigned int)primitive(0); const float x0 = projections(i0,0), y0 = projections(i0,1), z0 = (float)(Z+points(i0,2)); if (z0>zmin && x0>=0 && x0=0 && y0zmin && x0+radius>=0 && x0-radius=0 && y0-radiuszmin && z1>zmin && xM>=0 && xm=0 && ymxM) xM = x2; if (y0yM) yM = y2; if (z0>zmin && z1>zmin && z2>zmin && xM>=0 && xm=0 && ymxM) xM = x2; if (x3xM) xM = x3; if (y0yM) yM = y2; if (y3yM) yM = y3; if (z0>zmin && z1>zmin && z2>zmin && z3>zmin && xM>=0 && xm=0 && ym::draw_object3d() : Primitive %u is invalid (size = %u, can be 1,2,3,4,5,6,9 or 12)", pixel_type(),l,primitive.size()); } } } if (nb_visibles<=0) return *this; CImg permutations; CImg(zrange.data,nb_visibles,1,1,1,true).sort(permutations,false); // Compute light properties CImg lightprops; switch (render_type) { case 3: // Flat Shading { lightprops.assign(nb_visibles); cimg_forX(lightprops,l) { const CImg& primitive = primitives(visibles(permutations(l))); const unsigned int psize = primitive.size(); if (psize==3 || psize==4 || psize==9 || psize==12) { const unsigned int i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2); const float x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), dx1 = x1-x0, dy1 = y1-y0, dz1 = z1-z0, dx2 = x2-x0, dy2 = y2-y0, dz2 = z2-z0, nx = dy1*dz2-dz1*dy2, ny = dz1*dx2-dx1*dz2, nz = dx1*dy2-dy1*dx2, norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), lx = X+(x0+x1+x2)/3-lightx, ly = Y+(y0+y1+y2)/3-lighty, lz = Z+(z0+z1+z2)/3-lightz, nl = (float)std::sqrt(1e-5f+lx*lx+ly*ly+lz*lz), factor = cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl); lightprops[l] = cimg::max(factor,0.0f) + ambient_light; } else lightprops[l] = 1.0f; } } break; case 4: // Gouraud Shading case 5: // Phong-Shading { CImg points_normals(points.width,double_sided?7:3,1,1,0); for (unsigned int l=0; l& primitive = primitives[visibles(l)]; const unsigned int psize = primitive.size(); const bool triangle_flag = (psize==3) || (psize==9), rectangle_flag = (psize==4) || (psize==12); if (triangle_flag || rectangle_flag) { const unsigned int i0 = (unsigned int)primitive(0), i1 = (unsigned int)primitive(1), i2 = (unsigned int)primitive(2), i3 = rectangle_flag?(unsigned int)primitive(3):0; const float x0 = (float)points(i0,0), y0 = (float)points(i0,1), z0 = (float)points(i0,2), x1 = (float)points(i1,0), y1 = (float)points(i1,1), z1 = (float)points(i1,2), x2 = (float)points(i2,0), y2 = (float)points(i2,1), z2 = (float)points(i2,2), dx1 = x1-x0, dy1 = y1-y0, dz1 = z1-z0, dx2 = x2-x0, dy2 = y2-y0, dz2 = z2-z0, nnx = dy1*dz2-dz1*dy2, nny = dz1*dx2-dx1*dz2, nnz = dx1*dy2-dy1*dx2, norm = 1e-5f + (float)std::sqrt(nnx*nnx+nny*nny+nnz*nnz), nx = nnx/norm, ny = nny/norm, nz = nnz/norm; if (double_sided) { unsigned int ind = nz>0?3U:0U; const float incr = nz>0?-1.0f:1.0f; points_normals(i0, ind)+=nx; points_normals(i1,ind)+=nx; points_normals(i2,ind)+=nx; points_normals(i0,++ind)+=ny; points_normals(i1,ind)+=ny; points_normals(i2,ind)+=ny; points_normals(i0,++ind)+=nz; points_normals(i1,ind)+=nz; points_normals(i2,ind)+=nz; points_normals(i0,6)+=incr; points_normals(i1,6)+=incr; points_normals(i2,6)+=incr; if (rectangle_flag) { points_normals(i3,ind)+=nz; points_normals(i3,--ind)+=ny; points_normals(i3,--ind)+=nz; points_normals(i3,6)+=incr; } } else { points_normals(i0,0)+=nx; points_normals(i0,1)+=ny; points_normals(i0,2)+=nz; points_normals(i1,0)+=nx; points_normals(i1,1)+=ny; points_normals(i1,2)+=nz; points_normals(i2,0)+=nx; points_normals(i2,1)+=ny; points_normals(i2,2)+=nz; if (rectangle_flag) { points_normals(i3,0)+=nx; points_normals(i3,1)+=ny; points_normals(i3,2)+=nz; } } } } if (double_sided) cimg_forX(points_normals,l) if (points_normals(l,6)<0) { points_normals(l,0) = -points_normals(l,3); points_normals(l,1) = -points_normals(l,4); points_normals(l,2) = -points_normals(l,5); } if (render_type==4) { lightprops.assign(points.width); cimg_forX(points,ll) { const float nx = points_normals(ll,0), ny = points_normals(ll,1), nz = points_normals(ll,2), norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), lx = (float)(X+points(ll,0)-lightx), ly = (float)(Y+points(ll,1)-lighty), lz = (float)(Z+points(ll,2)-lightz), nl = (float)std::sqrt(1e-5f+lx*lx+ly*ly+lz*lz), factor = (-lx*nx-ly*ny-lz*nz)/(norm*nl); lightprops[ll] = cimg::max(factor,0.0f) + ambient_light; } } else { const unsigned int lw2 = light_texture.width/2-1, lh2 = light_texture.height/2-1; lightprops.assign(points.width,2); cimg_forX(points,ll) { const float nx = points_normals(ll,0), ny = points_normals(ll,1), nz = points_normals(ll,2), norm = (float)std::sqrt(1e-5f+nx*nx+ny*ny+nz*nz), nnx = nx/norm, nny = ny/norm; lightprops(ll,0) = lw2*(1+nnx); lightprops(ll,1) = lh2*(1+nny); } } } break; } // Draw visible primitives const unsigned int opacsize = opacities.size; { for (unsigned int l=0; l& primitive = primitives[n_primitive]; const CImg& color = colors[n_primitive%colors.size]; const CImg& opacity = opacities[n_primitive%opacsize]; const float opac = opacity.size()?(float)opacity(0):1.0f; switch (primitive.size()) { case 1: // Colored point or sprite { const unsigned int n0 = (unsigned int)primitive[0]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); if (color.size()==dim) draw_point(x0,y0,color,opac); else { const float z = Z + points(n0,2); const int factor = (int)(focale*100/(z+focale)), sw = color.width*factor/200, sh = color.height*factor/200; if (x0+sw>=0 && x0-sw=0 && y0-sh sprite = color.get_resize(-factor,-factor,1,-100,render_type<=3?1:3); if (opacity.width==color.width && opacity.height==color.height) draw_image(sprite,opacity.get_resize(sprite.width,sprite.height,1,sprite.dim,1),x0-sw,y0-sh,0,0); else draw_image(sprite,x0-sw,y0-sh,0,0,opac); } } } break; case 2: // Colored line { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); if (render_type) draw_line(x0,y0,x1,y1,color,opac); else draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac); } break; case 5: // Colored sphere { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1); int radius; if (n2) radius = (int)(n2*focale/(Z+points(n0,2)+focale)); else { const int x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), deltax = x1-x0, deltay = y1-y0; radius = (int)std::sqrt((float)(deltax*deltax + deltay*deltay)); } switch (render_type) { case 0: draw_point(x0,y0,color,opac); break; case 1: draw_circle(x0,y0,radius,color,opac,~0U); break; default: draw_circle(x0,y0,radius,color,opac); break; } } break; case 6: // Textured line { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], tx0 = (unsigned int)primitive[2], ty0 = (unsigned int)primitive[3], tx1 = (unsigned int)primitive[4], ty1 = (unsigned int)primitive[5]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale; if (render_type) draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac); else draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac); } break; case 3: // Colored triangle { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); switch(render_type) { case 0: draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac).draw_point(x2,y2,color,opac); break; case 1: draw_line(x0,y0,x1,y1,color,opac).draw_line(x0,y0,x2,y2,color,opac). draw_line(x1,y1,x2,y2,color,opac); break; case 2: draw_triangle(x0,y0,x1,y1,x2,y2,color,opac); break; case 3: _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)); break; case 4: draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprops(n0),lightprops(n1),lightprops(n2),opac); break; case 5: const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1); draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac); break; } } break; case 4: // Colored rectangle { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], n3 = (unsigned int)primitive[3]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); switch(render_type) { case 0: draw_point(x0,y0,color,opac).draw_point(x1,y1,color,opac). draw_point(x2,y2,color,opac).draw_point(x3,y3,color,opac); break; case 1: draw_line(x0,y0,x1,y1,color,opac).draw_line(x1,y1,x2,y2,color,opac). draw_line(x2,y2,x3,y3,color,opac).draw_line(x3,y3,x0,y0,color,opac); break; case 2: draw_triangle(x0,y0,x1,y1,x2,y2,color,opac).draw_triangle(x0,y0,x2,y2,x3,y3,color,opac); break; case 3: _draw_triangle(x0,y0,x1,y1,x2,y2,color.data,opac,lightprops(l)). _draw_triangle(x0,y0,x2,y2,x3,y3,color.data,opac,lightprops(l)); break; case 4: { const float lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); draw_triangle(x0,y0,x1,y1,x2,y2,color,lightprop0,lightprop1,lightprop2,opac). draw_triangle(x0,y0,x2,y2,x3,y3,color,lightprop0,lightprop2,lightprop3,opac); } break; case 5: { const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); draw_triangle(x0,y0,x1,y1,x2,y2,color,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(x0,y0,x2,y2,x3,y3,color,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); } break; } } break; case 9: // Textured triangle { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], tx0 = (unsigned int)primitive[3], ty0 = (unsigned int)primitive[4], tx1 = (unsigned int)primitive[5], ty1 = (unsigned int)primitive[6], tx2 = (unsigned int)primitive[7], ty2 = (unsigned int)primitive[8]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale; switch(render_type) { case 0: draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac); break; case 1: draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac). draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac); break; case 2: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac); break; case 3: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)); break; case 4: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac); break; case 5: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture, (unsigned int)lightprops(n0,0), (unsigned int)lightprops(n0,1), (unsigned int)lightprops(n1,0), (unsigned int)lightprops(n1,1), (unsigned int)lightprops(n2,0), (unsigned int)lightprops(n2,1), opac); break; } } break; case 12: // Textured rectangle { const unsigned int n0 = (unsigned int)primitive[0], n1 = (unsigned int)primitive[1], n2 = (unsigned int)primitive[2], n3 = (unsigned int)primitive[3], tx0 = (unsigned int)primitive[4], ty0 = (unsigned int)primitive[5], tx1 = (unsigned int)primitive[6], ty1 = (unsigned int)primitive[7], tx2 = (unsigned int)primitive[8], ty2 = (unsigned int)primitive[9], tx3 = (unsigned int)primitive[10], ty3 = (unsigned int)primitive[11]; const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1), x1 = (int)projections(n1,0), y1 = (int)projections(n1,1), x2 = (int)projections(n2,0), y2 = (int)projections(n2,1), x3 = (int)projections(n3,0), y3 = (int)projections(n3,1); const float z0 = points(n0,2) + Z + focale, z1 = points(n1,2) + Z + focale, z2 = points(n2,2) + Z + focale, z3 = points(n3,2) + Z + focale; switch(render_type) { case 0: draw_point(x0,y0,color.get_vector_at(tx0,ty0),opac). draw_point(x1,y1,color.get_vector_at(tx1,ty1),opac). draw_point(x2,y2,color.get_vector_at(tx2,ty2),opac). draw_point(x3,y3,color.get_vector_at(tx3,ty3),opac); break; case 1: draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac). draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac). draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac). draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac); break; case 2: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac); break; case 3: draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l)); break; case 4: { const float lightprop0 = lightprops(n0), lightprop1 = lightprops(n1), lightprop2 = lightprops(n2), lightprop3 = lightprops(n3); draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac); } break; case 5: { const unsigned int lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1), lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1), lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1), lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1); draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac). draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac); } break; } } break; } } } return *this; } //! Draw a 3D object in the instance image template CImg& draw_object3d(const float X, const float Y, const float Z, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float ambient_light=0.05f) { if (!points) return *this; CImg npoints(points.size,3,1,1,0); tp *ptrX = npoints.data, *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); cimg_forX(npoints,l) { const CImg& point = points[l]; const unsigned int siz = point.size(); if (!siz) throw CImgArgumentException("CImg<%s>::draw_object3d() : Given points (size=%u) contains a null element at " "position %u.",pixel_type(),points.size,l); *(ptrZ++) = (siz>2)?point(2):0; *(ptrY++) = (siz>1)?point(1):0; *(ptrX++) = point(0); } return draw_object3d(X,Y,Z,npoints,primitives,colors,opacities, render_type,double_sided,focale,lightx,lighty,lightz,ambient_light); } //! Draw a 3D object in the instance image template CImg& draw_object3d(const float X, const float Y, const float Z, const CImg& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float ambient_light=0.05f) { CImgList nopacities(opacities.size(),1); cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); return draw_object3d(X,Y,Z,points,primitives,colors,nopacities, render_type,double_sided,focale,lightx,lighty,lightz,ambient_light); } //! Draw a 3D object in the instance image template CImg& draw_object3d(const float X, const float Y, const float Z, const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float ambient_light=0.05f) { CImgList nopacities(opacities.size(),1); { cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); } if (!points) return *this; CImg npoints(points.size,3,1,1,0); tp *ptrX = npoints.data, *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); cimg_forX(npoints,l) { const CImg& point = points[l]; const unsigned int siz = point.size(); if (!siz) throw CImgArgumentException("CImg<%s>::draw_object3d() : Given points (size=%u) contains a null element at " "position %u.",pixel_type(),points.size,l); *(ptrZ++) = (siz>2)?point(2):0; *(ptrY++) = (siz>1)?point(1):0; *(ptrX++) = point(0); } return draw_object3d(X,Y,Z,npoints,primitives,colors,nopacities, render_type,double_sided,focale,lightx,lighty,lightz,ambient_light); } //! Draw a 3D object in the instance image template CImg& draw_object3d(const float X, const float Y, const float Z, const tp& points, const CImgList& primitives, const CImgList& colors, const unsigned int render_type=4, const bool double_sided=false, const float focale=500, const float lightx=0, const float lighty=0, const float lightz=-5000, const float ambient_light=0.05f, const float opacity=1.0f) { return draw_object3d(X,Y,Z,points,primitives,colors, CImg(primitives.size,1,1,1,opacity), render_type,double_sided,focale,lightx,lighty,lightz, ambient_light); } //@} //---------------------------- // //! \name Image Filtering //@{ //---------------------------- //! Compute the correlation of the instance image by a mask. /** The correlation of the instance image \p *this by the mask \p mask is defined to be : res(x,y,z) = sum_{i,j,k} (*this)(x+i,y+j,z+k)*mask(i,j,k) \param mask = the correlation kernel. \param cond = the border condition type (0=zero, 1=dirichlet) \param weighted_correl = enable local normalization. **/ template CImg::type> get_correlate(const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) const { typedef typename cimg::superset2::type ftype; if (is_empty()) return CImg(); if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::correlate() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); if (cond && mask.width==mask.height && ((mask.depth==1 && mask.width<=5) || (mask.depth==mask.width && mask.width<=3))) { // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with cond=1) switch (mask.depth) { case 3: { T I[27] = { 0 }; cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3x3(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] + I[25]*I[25] + I[26]*I[26]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 2: { T I[8] = { 0 }; cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + I[6]*mask[6] + I[7]*mask[7]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2x2(*this,x,y,z,v,I) { const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + I[6]*I[6] + I[7]*I[7]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; default: case 1: switch (mask.width) { case 6: { T I[36] = { 0 }; cimg_forZV(*this,z,v) cimg_for6x6(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24] + I[25]*mask[25] + I[26]*mask[26] + I[27]*mask[27] + I[28]*mask[28] + I[29]*mask[29] + I[30]*mask[30] + I[31]*mask[31] + I[32]*mask[32] + I[33]*mask[33] + I[34]*mask[34] + I[35]*mask[35]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] + I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 5: { T I[25] = { 0 }; cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15] + I[16]*mask[16] + I[17]*mask[17] + I[18]*mask[18] + I[19]*mask[19] + I[20]*mask[20] + I[21]*mask[21] + I[22]*mask[22] + I[23]*mask[23] + I[24]*mask[24]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for5x5(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 4: { T I[16] = { 0 }; cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[ 0]*mask[ 0] + I[ 1]*mask[ 1] + I[ 2]*mask[ 2] + I[ 3]*mask[ 3] + I[ 4]*mask[ 4] + I[ 5]*mask[ 5] + I[ 6]*mask[ 6] + I[ 7]*mask[ 7] + I[ 8]*mask[ 8] + I[ 9]*mask[ 9] + I[10]*mask[10] + I[11]*mask[11] + I[12]*mask[12] + I[13]*mask[13] + I[14]*mask[14] + I[15]*mask[15]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for4x4(*this,x,y,z,v,I) { const double weight = (double)(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 3: { T I[9] = { 0 }; cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] + I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for3x3(*this,x,y,z,v,I) { const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + I[3]*I[3] + I[4]*I[4] + I[5]*I[5] + I[6]*I[6] + I[7]*I[7] + I[8]*I[8]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 2: { T I[4] = { 0 }; cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) dest(x,y,z,v) = (ftype) (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] + I[3]*mask[3]); if (weighted_correl) cimg_forZV(*this,z,v) cimg_for2x2(*this,x,y,z,v,I) { const double weight = (double)(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] + I[3]*I[3]); if (weight>0) dest(x,y,z,v)/=(ftype)std::sqrt(weight); } } break; case 1: (dest.assign(*this))*=mask(0); break; } } } else { // Generic version for other masks const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); cimg_forV(*this,v) if (!weighted_correl) // Classical correlation { for (int z=czm; z=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { ftype val = 0; for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) val+=pix3d(x+xm,y+ym,z+zm,v)*mask(cxm+xm,cym+ym,czm+zm,0); dest(x,y,z,v) = (ftype)val; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { ftype val = 0; for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) val+=pix3d(x+xm,y+ym,z+zm,v,0)*mask(cxm+xm,cym+ym,czm+zm,0); dest(x,y,z,v) = (ftype)val; } } else // Weighted correlation { for (int z=czm; z0)?(ftype)(val/std::sqrt((double)weight)):0; } if (cond) cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { ftype val = 0, weight = 0; for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) { const T cval = pix3d(x+xm,y+ym,z+zm,v); val+=cval*mask(cxm+xm,cym+ym,czm+zm,0); weight+=cval*cval; } dest(x,y,z,v) = (weight>0)?(ftype)(val/std::sqrt((double)weight)):0; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { ftype val = 0, weight = 0; for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) { const T cval = pix3d(x+xm,y+ym,z+zm,v,0); val+=cval*mask(cxm+xm,cym+ym,czm+zm,0); weight+=cval*cval; } dest(x,y,z,v) = (weight>0)?(ftype)(val/std::sqrt((double)weight)):0; } } } return dest; } //! In-place version of the previous function. template CImg& correlate(const CImg& mask, const unsigned int cond=1, const bool weighted_correl=false) { return get_correlate(mask,cond,weighted_correl).assign_to(*this); } //! Return the convolution of the image by a mask /** The result \p res of the convolution of an image \p img by a mask \p mask is defined to be : res(x,y,z) = sum_{i,j,k} img(x-i,y-j,z-k)*mask(i,j,k) \param mask = the correlation kernel. \param cond = the border condition type (0=zero, 1=dirichlet) \param weighted_convol = enable local normalization. **/ template CImg::type> get_convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) const { typedef typename cimg::superset2::type ftype; if (is_empty()) return CImg(); if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::convolve() : Specified mask (%u,%u,%u,%u,%p) is not scalar.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); return get_correlate(mask.get_mirror('x').mirror('y').mirror('z'),cond,weighted_convol); } //! In-place version of the previous function. template CImg& convolve(const CImg& mask, const unsigned int cond=1, const bool weighted_convol=false) { return get_convolve(mask,cond,weighted_convol).assign_to(*this); } //! Return the erosion of the image by a structuring element. template CImg::type> get_erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::erode() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); cimg_forV(*this,v) if (!weighted_erosion) // Classical erosion { for (int z=czm; z::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)(*this)(x+xm,y+ym,z+zm,v),min_val); dest(x,y,z,v) = min_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype min_val = cimg::type::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)pix3d(x+xm,y+ym,z+zm,v),min_val); dest(x,y,z,v) = min_val; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype min_val = cimg::type::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) min_val = cimg::min((restype)pix3d(x+xm,y+ym,z+zm,v,0),min_val); dest(x,y,z,v) = min_val; } } else // Weighted erosion { t mval=0; for (int z=czm; z::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)((*this)(x+xm,y+ym,z+zm,v)+mval),min_val); dest(x,y,z,v) = min_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype min_val = cimg::type::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)(pix3d(x+xm,y+ym,z+zm,v)+mval),min_val); dest(x,y,z,v) = min_val; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype min_val = cimg::type::max(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) min_val = cimg::min((restype)(pix3d(x+xm,y+ym,z+zm,v,0)+mval),min_val); dest(x,y,z,v) = min_val; } } return dest; } //! In-place version of the previous function. template CImg& erode(const CImg& mask, const unsigned int cond=1, const bool weighted_erosion=false) { return get_erode(mask,cond,weighted_erosion).assign_to(*this); } //! Erode the image by a square structuring element of size n CImg get_erode(const unsigned int n, const unsigned int cond=1) const { static CImg mask; if (n<2) return *this; if (mask.width!=n) mask.assign(n,n,1,1,1); const CImg res = get_erode(mask,cond,false); if (n>20) mask.assign(); return res; } //! In-place version of the previous function. CImg& erode(const unsigned int n, const unsigned int cond=1) { if (n<2) return *this; return get_erode(n,cond).assign_to(*this); } //! Return the dilatation of the image by a structuring element. template CImg::type> get_dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) const { typedef typename cimg::superset::type restype; if (is_empty()) return CImg(); if (!mask || mask.dim!=1) throw CImgArgumentException("CImg<%s>::dilate() : Specified mask (%u,%u,%u,%u,%p) is not a scalar image.", pixel_type(),mask.width,mask.height,mask.depth,mask.dim,mask.data); CImg dest(width,height,depth,dim); const int cxm=mask.width/2, cym=mask.height/2, czm=mask.depth/2, fxm=cxm-1+(mask.width%2), fym=cym-1+(mask.height%2), fzm=czm-1+(mask.depth%2); cimg_forV(*this,v) if (!weighted_dilatation) // Classical dilatation { for (int z=czm; z::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)(*this)(x+xm,y+ym,z+zm,v),max_val); dest(x,y,z,v) = max_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype max_val = cimg::type::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)pix3d(x+xm,y+ym,z+zm,v),max_val); dest(x,y,z,v) = max_val; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype max_val = cimg::type::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if (mask(cxm+xm,cym+ym,czm+zm,0)) max_val = cimg::max((restype)pix3d(x+xm,y+ym,z+zm,v,0),max_val); dest(x,y,z,v) = max_val; } } else // Weighted dilatation { t mval=0; for (int z=czm; z::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)((*this)(x+xm,y+ym,z+zm,v)-mval),max_val); dest(x,y,z,v) = max_val; } if (cond) cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype max_val = cimg::type::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)(pix3d(x+xm,y+ym,z+zm,v)-mval),max_val); dest(x,y,z,v) = max_val; } else cimg_forYZV(*this,y,z,v) for (int x=0; x=dimy()-cym || z=dimz()-czm)?x++:((x=dimx()-cxm)?x++:(x=dimx()-cxm))) { restype max_val = cimg::type::min(); for (int zm=-czm; zm<=fzm; ++zm) for (int ym=-cym; ym<=fym; ++ym) for (int xm=-cxm; xm<=fxm; ++xm) if ((mval=mask(cxm+xm,cym+ym,czm+zm,0))!=0) max_val = cimg::max((restype)(pix3d(x+xm,y+ym,z+zm,v,0)-mval),max_val); dest(x,y,z,v) = max_val; } } return dest; } template CImg& dilate(const CImg& mask, const unsigned int cond=1, const bool weighted_dilatation=false) { return get_dilate(mask,cond,weighted_dilatation).assign_to(*this); } //! Dilate the image by a square structuring element of size n CImg get_dilate(const unsigned int n, const unsigned int cond=1) const { static CImg mask; if (n<2) return *this; if (mask.width!=n) mask.assign(n,n,1,1,1); const CImg res = get_dilate(mask,cond,false); if (n>20) mask.assign(); return res; } //! In-place version of the previous function. CImg& dilate(const unsigned int n, const unsigned int cond=1) { if (n<2) return *this; return get_dilate(n,cond).assign_to(*this); } //! Add noise to the image. /** \param sigma = power of the noise. if sigma<0, it corresponds to the percentage of the maximum image value. \param ntype = noise type. can be 0=gaussian, 1=uniform or 2=Salt and Pepper. \return A noisy version of the instance image. **/ CImg get_noise(const double sigma=-20, const unsigned int ntype=0) const { return (+*this).noise(sigma,ntype); } //! In-place version of the previous function. CImg& noise(const double sigma=-20, const unsigned int ntype=0) { if (!is_empty()) { double nsigma = sigma, max = (double)cimg::type::max(), min = (double)cimg::type::min(); typedef typename cimg::superset::type ftype; cimg::srand(); ftype m = 0, M = 0; if (nsigma==0) return *this; if (nsigma<0 || ntype==2) m = (ftype)minmax(M); if (nsigma<0) nsigma = -nsigma*(M-m)/100.0; switch (ntype) { case 0: // Gaussian noise { cimg_for(*this,ptr,T) { double val = *ptr+nsigma*cimg::grand(); if (val>max) val = max; if (valmax) val = max; if (val100.0) *ptr = (T)(unsigned int)((std::sqrt(z) * cimg::grand()) + z); else { unsigned int k = 0; const double y=std::exp(-z); for (double s=1.0; s>=y; ++k) s *= cimg::rand(); *ptr = (T)(k-1); } } } } break; case 4: // Rice noise { const double sqrt2 = (double)std::sqrt(2.0); cimg_for(*this,ptr,T) { const double val0 = (double)*ptr/sqrt2, re = val0 + nsigma*cimg::grand(), im = val0 + nsigma*cimg::grand(); double val = std::sqrt(re*re + im*im); if (val>max) val = max; if (val::type> get_deriche(const float sigma, const int order=0, const char axe='x', const bool cond=true) const { typedef typename cimg::superset::type ftype; return CImg(*this,false).deriche(sigma,order,axe,cond); } //! In-place version of the previous function. CImg& deriche(const float sigma, const int order=0, const char axe='x', const bool cond=true) { #define cimg_deriche2_apply \ ftype *ptrY = Y.data, yb = 0, yp = 0; \ T xp = (T)0; \ if (cond) { xp = *ptrX; yb = yp = (ftype)(coefp*xp); } \ for (int m=0; m=0; --n) { \ const T xc = *(ptrX-=off); \ const ftype yc = (ftype)(a2*xn + a3*xa - b1*yn - b2*ya); \ xa = xn; xn = xc; ya = yn; yn = yc; \ *ptrX = (T)(*(--ptrY)+yc); \ } typedef typename cimg::superset::type ftype; if (sigma<0) throw CImgArgumentException("CImg<%s>::deriche() : Given filter variance (sigma = %g) is negative",pixel_type(),sigma); if (is_empty() || (sigma<0.1 && !order)) return *this; const float nsigma = sigma<0.1f?0.1f:sigma, alpha = 1.695f/nsigma, ema = (float)std::exp(-alpha), ema2 = (float)std::exp(-2*alpha), b1 = -2*ema, b2 = ema2; float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0; switch (order) { case 0: { const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2); a0 = k; a1 = k*(alpha-1)*ema; a2 = k*(alpha+1)*ema; a3 = -k*ema2; } break; case 1: { const float k = (1-ema)*(1-ema)/ema; a0 = k*ema; a1 = a3 = 0; a2 = -a0; } break; case 2: { const float ea = (float)std::exp(-alpha), k = -(ema2-1)/(2*alpha*ema), kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea)); a0 = kn; a1 = -kn*(1+k*alpha)*ema; a2 = kn*(1-k*alpha)*ema; a3 = -kn*ema2; } break; default: throw CImgArgumentException("CImg<%s>::deriche() : Given filter order (order = %u) must be 0,1 or 2",pixel_type(),order); break; } coefp = (a0+a1)/(1+b1+b2); coefn = (a2+a3)/(1+b1+b2); switch (cimg::uncase(axe)) { case 'x': { const int N = width, off = 1; CImg Y(N); cimg_forYZV(*this,y,z,v) { T *ptrX = ptr(0,y,z,v); cimg_deriche2_apply; } } break; case 'y': { const int N = height, off = width; CImg Y(N); cimg_forXZV(*this,x,z,v) { T *ptrX = ptr(x,0,z,v); cimg_deriche2_apply; } } break; case 'z': { const int N = depth, off = width*height; CImg Y(N); cimg_forXYV(*this,x,y,v) { T *ptrX = ptr(x,y,0,v); cimg_deriche2_apply; } } break; case 'v': { const int N = dim, off = width*height*depth; CImg Y(N); cimg_forXYZ(*this,x,y,z) { T *ptrX = ptr(x,y,z,0); cimg_deriche2_apply; } } break; } return *this; } //! Return a blurred version of the image, using a Canny-Deriche filter. /** Blur the image with an anisotropic exponential filter (Deriche filter of order 0). **/ CImg::type> get_blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) const { typedef typename cimg::superset::type ftype; return CImg(*this,false).blur(sigmax,sigmay,sigmaz,cond); } //! In-place version of the previous function. CImg& blur(const float sigmax, const float sigmay, const float sigmaz, const bool cond=true) { if (!is_empty()) { if (width>1 && sigmax>0) deriche(sigmax,0,'x',cond); if (height>1 && sigmay>0) deriche(sigmay,0,'y',cond); if (depth>1 && sigmaz>0) deriche(sigmaz,0,'z',cond); } return *this; } //! Return a blurred version of the image, using a Canny-Deriche filter. CImg::type> get_blur(const float sigma, const bool cond=true) const { typedef typename cimg::superset::type ftype; return CImg(*this,false).blur(sigma,cond); } //! In-place version of the previous function. CImg& blur(const float sigma, const bool cond=true) { return blur(sigma,sigma,sigma,cond); } //! Get a blurred version of an image following a field of diffusion tensors. /** \param G = Field of square roots of diffusion tensors used to drive the smoothing. \param amplitude = amplitude of the smoothing. \param dl = spatial discretization. \param da = angular discretization. \param gauss_prec = precision of the gaussian function. \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) \param fast_approx = Tell to use the fast approximation or not. **/ template CImg get_blur_anisotropic(const CImg& G, const float amplitude=60.0f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true) const { return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); } //! In-place version of the previous function. template CImg& blur_anisotropic(const CImg& G, const float amplitude=60.0f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true) { #define cimg_valign2d(i,j) \ { ftype &u = W(i,j,0,0), &v = W(i,j,0,1); \ if (u*curru + v*currv<0) { u=-u; v=-v; }} #define cimg_valign3d(i,j,k) \ { ftype &u = W(i,j,k,0), &v = W(i,j,k,1), &w = W(i,j,k,2); \ if (u*curru + v*currv + w*currw<0) { u=-u; v=-v; w=-w; }} // Check arguments and init variables typedef typename cimg::superset::type ftype; if (!is_empty() && amplitude>0) { if (!G || (G.dim!=3 && G.dim!=6) || G.width!=width || G.height!=height || G.depth!=depth) throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Specified tensor field (%u,%u,%u,%u) is not valid.", pixel_type(),G.width,G.height,G.depth,G.dim); const float sqrt2amplitude = (float)std::sqrt(2*amplitude); const bool threed = (G.dim>=6); const int dx1 = dimx()-1, dy1 = dimy()-1, dz1 = dimz()-1; CImg dest(width,height,depth,dim,0), W(width,height,depth,threed?4:3), tmp(dim); int N = 0; if (threed) // 3D version of the algorithm for (float phi=(180%(int)da)/2.0f; phi<=180; phi+=da) { const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp; for (float theta=0; theta<360; (theta+=da2),++N) { const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)*std::cos(phir)), vy = (float)(std::sin(thetar)*std::cos(phir)), vz = (float)std::sin(phir); const t *pa = G.ptr(0,0,0,0), *pb = G.ptr(0,0,0,1), *pc = G.ptr(0,0,0,2), *pd = G.ptr(0,0,0,3), *pe = G.ptr(0,0,0,4), *pf = G.ptr(0,0,0,5); ftype *pd0 = W.ptr(0,0,0,0), *pd1 = W.ptr(0,0,0,1), *pd2 = W.ptr(0,0,0,2), *pd3 = W.ptr(0,0,0,3); cimg_forXYZ(G,xg,yg,zg) { const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++); const float u = (float)(a*vx + b*vy + c*vz), v = (float)(b*vx + d*vy + e*vz), w = (float)(c*vx + e*vy + f*vz), n = (float)std::sqrt(1e-5+u*u+v*v+w*w), dln = dl/n; *(pd0++) = (ftype)(u*dln); *(pd1++) = (ftype)(v*dln); *(pd2++) = (ftype)(w*dln); *(pd3++) = (ftype)n; } cimg_forXYZ(*this,x,y,z) { tmp.fill(0); const float cu = (float)W(x,y,z,0), cv = (float)W(x,y,z,1), cw = (float)W(x,y,z,2), n = (float)W(x,y,z,3), fsigma = (float)(n*sqrt2amplitude), length = gauss_prec*fsigma, fsigma2 = 2*fsigma*fsigma; float S = 0, pu = cu, pv = cv, pw = cw, X = (float)x, Y = (float)y, Z = (float)z; switch (interpolation) { case 0: { // Nearest neighbor for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)(X+0.5f), cy = (int)(Y+0.5f), cz = (int)(Z+0.5f); float u = (float)W(cx,cy,cz,0), v = (float)W(cx,cy,cz,1), w = (float)W(cx,cy,cz,2); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)(*this)(cx,cy,cz,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*(*this)(cx,cy,cz,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; case 1: { // Linear interpolation for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; const float curru = (float)W(cx,cy,cz,0), currv = (float)W(cx,cy,cz,1), currw = (float)W(cx,cy,cz,2); cimg_valign3d(px,py,pz); cimg_valign3d(cx,py,pz); cimg_valign3d(nx,py,pz); cimg_valign3d(px,cy,pz); cimg_valign3d(cx,cy,pz); cimg_valign3d(nx,cy,pz); cimg_valign3d(px,ny,pz); cimg_valign3d(cx,ny,pz); cimg_valign3d(nx,ny,pz); cimg_valign3d(px,py,cz); cimg_valign3d(cx,py,cz); cimg_valign3d(nx,py,cz); cimg_valign3d(px,cy,cz); cimg_valign3d(nx,cy,cz); cimg_valign3d(px,ny,cz); cimg_valign3d(cx,ny,cz); cimg_valign3d(nx,ny,cz); cimg_valign3d(px,py,nz); cimg_valign3d(cx,py,nz); cimg_valign3d(nx,py,nz); cimg_valign3d(px,cy,nz); cimg_valign3d(cx,cy,nz); cimg_valign3d(nx,cy,nz); cimg_valign3d(px,ny,nz); cimg_valign3d(cx,ny,nz); cimg_valign3d(nx,ny,nz); float u = (float)(W.linear_pix3d(X,Y,Z,0)), v = (float)(W.linear_pix3d(X,Y,Z,1)), w = (float)(W.linear_pix3d(X,Y,Z,2)); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix3d(X,Y,Z,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix3d(X,Y,Z,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; default: { // 2nd order Runge Kutta for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1, cz = (int)Z, pz = (cz-1<0)?0:cz-1, nz = (cz+1>dz1)?dz1:cz+1; const float curru = (float)W(cx,cy,cz,0), currv = (float)W(cx,cy,cz,1), currw = (float)W(cx,cy,cz,2); cimg_valign3d(px,py,pz); cimg_valign3d(cx,py,pz); cimg_valign3d(nx,py,pz); cimg_valign3d(px,cy,pz); cimg_valign3d(cx,cy,pz); cimg_valign3d(nx,cy,pz); cimg_valign3d(px,ny,pz); cimg_valign3d(cx,ny,pz); cimg_valign3d(nx,ny,pz); cimg_valign3d(px,py,cz); cimg_valign3d(cx,py,cz); cimg_valign3d(nx,py,cz); cimg_valign3d(px,cy,cz); cimg_valign3d(nx,cy,cz); cimg_valign3d(px,ny,cz); cimg_valign3d(cx,ny,cz); cimg_valign3d(nx,ny,cz); cimg_valign3d(px,py,nz); cimg_valign3d(cx,py,nz); cimg_valign3d(nx,py,nz); cimg_valign3d(px,cy,nz); cimg_valign3d(cx,cy,nz); cimg_valign3d(nx,cy,nz); cimg_valign3d(px,ny,nz); cimg_valign3d(cx,ny,nz); cimg_valign3d(nx,ny,nz); const float u0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,0)), v0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,1)), w0 = (float)(0.5f*W.linear_pix3d(X,Y,Z,2)); float u = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,0)), v = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,1)), w = (float)(W.linear_pix3d(X+u0,Y+v0,Z+w0,2)); if ((pu*u + pv*v + pw*w)<0) { u=-u; v=-v; w=-w; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix3d(X,Y,Z,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix3d(X,Y,Z,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); Z+=(pw=w); } } break; } if (S>0) cimg_forV(dest,k) dest(x,y,z,k)+=tmp[k]/S; else cimg_forV(dest,k) dest(x,y,z,k)+=(ftype)((*this)(x,y,z,k)); #ifdef cimg_plugin_greycstoration if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this; #endif } } } else // 2D version of the algorithm for (float theta=(360%(int)da)/2.0f; theta<360; (theta+=da),++N) { const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar)); const t *pa = G.ptr(0,0,0,0), *pb = G.ptr(0,0,0,1), *pc = G.ptr(0,0,0,2); ftype *pd0 = W.ptr(0,0,0,0), *pd1 = W.ptr(0,0,0,1), *pd2 = W.ptr(0,0,0,2); cimg_forXY(G,xg,yg) { const t a = *(pa++), b = *(pb++), c = *(pc++); const float u = (float)(a*vx + b*vy), v = (float)(b*vx + c*vy), n = (float)std::sqrt(1e-5+u*u+v*v), dln = dl/n; *(pd0++) = (ftype)(u*dln); *(pd1++) = (ftype)(v*dln); *(pd2++) = (ftype)n; } cimg_forXY(*this,x,y) { tmp.fill(0); const float cu = (float)W(x,y,0,0), cv = (float)W(x,y,0,1), n = (float)W(x,y,0,2), fsigma = (float)(n*sqrt2amplitude), length = gauss_prec*fsigma, fsigma2 = 2*fsigma*fsigma; float S = 0, pu = cu, pv = cv, X = (float)x, Y = (float)y; switch (interpolation) { case 0: { // Nearest-neighbor interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)(X+0.5f), cy = (int)(Y+0.5f); float u = (float)W(cx,cy,0,0), v = (float)W(cx,cy,0,1); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)(*this)(cx,cy,0,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*(*this)(cx,cy,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } break; case 1: { // Linear interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; const float curru = (float)W(cx,cy,0,0), currv = (float)W(cx,cy,0,1); cimg_valign2d(px,py); cimg_valign2d(cx,py); cimg_valign2d(nx,py); cimg_valign2d(px,cy); cimg_valign2d(nx,cy); cimg_valign2d(px,ny); cimg_valign2d(cx,ny); cimg_valign2d(nx,ny); float u = (float)(W.linear_pix2d(X,Y,0,0)), v = (float)(W.linear_pix2d(X,Y,0,1)); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix2d(X,Y,0,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix2d(X,Y,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } break; default: { // 2nd-order Runge-kutta interpolation for 2D images for (float l=0; l=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) { const int cx = (int)X, px = (cx-1<0)?0:cx-1, nx = (cx+1>dx1)?dx1:cx+1, cy = (int)Y, py = (cy-1<0)?0:cy-1, ny = (cy+1>dy1)?dy1:cy+1; const float curru = (float)W(cx,cy,0,0), currv = (float)W(cx,cy,0,1); cimg_valign2d(px,py); cimg_valign2d(cx,py); cimg_valign2d(nx,py); cimg_valign2d(px,cy); cimg_valign2d(nx,cy); cimg_valign2d(px,ny); cimg_valign2d(cx,ny); cimg_valign2d(nx,ny); const float u0 = (float)(0.5f*W.linear_pix2d(X,Y,0,0)), v0 = (float)(0.5f*W.linear_pix2d(X,Y,0,1)); float u = (float)(W.linear_pix2d(X+u0,Y+v0,0,0)), v = (float)(W.linear_pix2d(X+u0,Y+v0,0,1)); if ((pu*u + pv*v)<0) { u=-u; v=-v; } if (fast_approx) { cimg_forV(*this,k) tmp[k]+=(ftype)linear_pix2d(X,Y,0,k); ++S; } else { const float coef = (float)std::exp(-l*l/fsigma2); cimg_forV(*this,k) tmp[k]+=(ftype)(coef*linear_pix2d(X,Y,0,k)); S+=coef; } X+=(pu=u); Y+=(pv=v); } } break; } if (S>0) cimg_forV(dest,k) dest(x,y,0,k)+=tmp[k]/S; else cimg_forV(dest,k) dest(x,y,0,k)+=(ftype)((*this)(x,y,0,k)); #ifdef cimg_plugin_greycstoration if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this; #endif } } const ftype *ptrs = dest.data+dest.size(); const T m = cimg::type::min(), M = cimg::type::max(); cimg_for(*this,ptrd,T) { const ftype val = *(--ptrs)/N; *ptrd = valM?M:(T)val); } } return *this; } //! Blur an image in an anisotropic way. /** \param mask Binary mask. \param amplitude Amplitude of the anisotropic blur. \param sharpness Contour preservation. \param anisotropy Smoothing anisotropy. \param alpha Image pre-blurring (gaussian). \param sigma Regularity of the tensor-valued geometry. \param dl Spatial discretization. \param da Angular discretization. \param gauss_prec Precision of the gaussian function. \param interpolation Used interpolation scheme (0 = nearest-neighbor, 1 = linear, 2 = Runge-Kutta) \param fast_approx Tell to use the fast approximation or not \param geom_factor Geometry factor. **/ template CImg get_blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) const { return (+*this).blur_anisotropic(mask,amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); } //! In-place version of the previous function. template CImg& blur_anisotropic(const CImg& mask, const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) { if (!is_empty() && amplitude>0) { if (amplitude==0) return *this; if (amplitude<0 || sharpness<0 || anisotropy<0 || anisotropy>1 || alpha<0 || sigma<0 || dl<0 || da<0 || gauss_prec<0) throw CImgArgumentException("CImg<%s>::blur_anisotropic() : Given parameters are amplitude(%g), sharpness(%g), " "anisotropy(%g), alpha(%g), sigma(%g), dl(%g), da(%g), gauss_prec(%g).\n" "Admissible parameters are in the range : amplitude>0, sharpness>0, anisotropy in [0,1], " "alpha>0, sigma>0, dl>0, da>0, gauss_prec>0.", pixel_type(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec); const bool threed = (depth>1), no_mask = mask.is_empty(); const float nsharpness = cimg::max(sharpness,1e-5f), power1 = 0.5f*nsharpness, power2 = power1/(1e-7f+1.0f-anisotropy); CImg blurred = CImg(*this,false).blur(alpha); if (geom_factor>0) blurred*=geom_factor; else blurred.normalize(0,-geom_factor); if (threed) // Field for 3D volumes { #ifdef cimg_plugin_greycstoration greycstoration_mutex_lock(greycstoration_params[0]); #endif CImg val(3), vec(3,3), G(blurred.get_structure_tensorXYZ()); if (sigma>0) G.blur(sigma); cimg_forXYZ(*this,x,y,z) { if (no_mask || mask(x,y,z)) { G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); const float l1 = val[2], l2 = val[1], l3 = val[0], ux = vec(0,0), uy = vec(0,1), uz = vec(0,2), vx = vec(1,0), vy = vec(1,1), vz = vec(1,2), wx = vec(2,0), wy = vec(2,1), wz = vec(2,2), n1 = (float)std::pow(1.0f+l1+l2+l3,-power1), n2 = (float)std::pow(1.0f+l1+l2+l3,-power2); G(x,y,z,0) = n1*(ux*ux + vx*vx) + n2*wx*wx; G(x,y,z,1) = n1*(ux*uy + vx*vy) + n2*wx*wy; G(x,y,z,2) = n1*(ux*uz + vx*vz) + n2*wx*wz; G(x,y,z,3) = n1*(uy*uy + vy*vy) + n2*wy*wy; G(x,y,z,4) = n1*(uy*uz + vy*vz) + n2*wy*wz; G(x,y,z,5) = n1*(uz*uz + vz*vz) + n2*wz*wz; } else G(x,y,z,0) = G(x,y,z,1) = G(x,y,z,2) = G(x,y,z,3) = G(x,y,z,4) = G(x,y,z,5) = 0; #ifdef cimg_plugin_greycstoration if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this; #endif } #ifdef cimg_plugin_greycstoration greycstoration_mutex_unlock(greycstoration_params[0]); #endif blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); } else // Field for 2D images { #ifdef cimg_plugin_greycstoration greycstoration_mutex_lock(greycstoration_params[0]); #endif CImg val(2), vec(2,2), G(blurred.get_structure_tensorXY()); if (sigma>0) G.blur(sigma); cimg_forXY(*this,x,y) { if (no_mask || mask(x,y)) { G.get_tensor_at(x,y).symmetric_eigen(val,vec); const float l1 = val[1], l2 = val[0], ux = vec(1,0), uy = vec(1,1), vx = vec(0,0), vy = vec(0,1), n1 = (float)std::pow(1.0f+l1+l2,-power1), n2 = (float)std::pow(1.0f+l1+l2,-power2); G(x,y,0,0) = n1*ux*ux + n2*vx*vx; G(x,y,0,1) = n1*ux*uy + n2*vx*vy; G(x,y,0,2) = n1*uy*uy + n2*vy*vy; } else G(x,y,0,0) = G(x,y,0,1) = G(x,y,0,2) = 0; #ifdef cimg_plugin_greycstoration if (!*(greycstoration_params->stop_request)) ++(*greycstoration_params->counter); else return *this; #endif } #ifdef cimg_plugin_greycstoration greycstoration_mutex_unlock(greycstoration_params[0]); #endif blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation,fast_approx); } } return *this; } //! Blur an image following in an anisotropic way. CImg get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) const { return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); } //! In-place version of the previous function. CImg& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.3f, const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30.0f, const float gauss_prec=2.0f, const unsigned int interpolation=0, const bool fast_approx=true, const float geom_factor=1.0f) { return blur_anisotropic(CImg(),amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation,fast_approx,geom_factor); } //! Blur an image using the bilateral filter. /** \param sigmax Amount of blur along the X-axis. \param sigmay Amount of blur along the Y-axis. \param sigmaz Amount of blur along the Z-axis. \param sigmar Amount of blur along the range axis. \param bgridx Size of the bilateral grid along the X-axis. \param bgridy Size of the bilateral grid along the Y-axis. \param bgridz Size of the bilateral grid along the Z-axis. \param bgridr Size of the bilateral grid along the range axis. \param interpolation Use interpolation for image slicing. \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006 (extended for 3D volumetric images). **/ CImg get_blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, const int bgridx, const int bgridy, const int bgridz, const int bgridr, const bool interpolation=true) const { return (+*this).blur_bilateral(sigmax,sigmay,sigmaz,sigmar,bgridx,bgridy,bgridz,bgridr,interpolation); } //! In-place version of the previous function. CImg& blur_bilateral(const float sigmax, const float sigmay, const float sigmaz, const float sigmar, const int bgridx, const int bgridy, const int bgridz, const int bgridr, const bool interpolation=true) { T m, M = maxmin(m); const float range = (float)(1.0f+M-m); const unsigned int bx0 = bgridx>=0?bgridx:width*(-bgridx)/100, by0 = bgridy>=0?bgridy:height*(-bgridy)/100, bz0 = bgridz>=0?bgridz:depth*(-bgridz)/100, br0 = bgridr>=0?bgridr:(int)(-range*bgridr/100), bx = bx0>0?bx0:1, by = by0>0?by0:1, bz = bz0>0?bz0:1, br = br0>0?br0:1; const float nsigmax = sigmax*bx/width, nsigmay = sigmay*by/height, nsigmaz = sigmaz*bz/depth, nsigmar = sigmar*br/range; if (nsigmax>0 || nsigmay>0 || nsigmaz>0 || nsigmar>0) { const bool threed = depth>1; if (threed) // 3d version of the algorithm { typedef typename cimg::last::type ftype; CImg bgrid(bx,by,bz,br), bgridw(bx,by,bz,br); cimg_forV(*this,k) { bgrid.fill(0); bgridw.fill(0); cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); bgrid(X,Y,Z,R) = (float)val; bgridw(X,Y,Z,R) = 1; } bgrid.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); bgridw.blur(nsigmax,nsigmay,nsigmaz,true).deriche(nsigmar,0,'v',false); if (interpolation) cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const float X = (float)x*bx/width, Y = (float)y*by/height, Z = (float)z*bz/depth, R = (val-m)*br/range, bval0 = bgrid.linear_pix4d(X,Y,Z,R), bval1 = bgridw.linear_pix4d(X,Y,Z,R); (*this)(x,y,z,k) = (T)(bval0/bval1); } else cimg_forXYZ(*this,x,y,z) { const T val = (*this)(x,y,z,k); const int X = x*bx/width, Y = y*by/height, Z = z*bz/depth, R = (int)((val-m)*br/range); const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R); (*this)(x,y,z,k) = (T)(bval0/bval1); } } } else // 2d version of the algorithm { typedef typename cimg::last::type ftype; CImg bgrid(bx,by,br,2); cimg_forV(*this,k) { bgrid.fill(0); cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); bgrid(X,Y,R,0) = (float)val; bgrid(X,Y,R,1) = 1; } bgrid.blur(nsigmax,nsigmay,0,true).blur(0,0,nsigmar,false); if (interpolation) cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const float X = (float)x*bx/width, Y = (float)y*by/height, R = (val-m)*br/range, bval0 = bgrid.linear_pix3d(X,Y,R,0), bval1 = bgrid.linear_pix3d(X,Y,R,1); (*this)(x,y,k) = (T)(bval0/bval1); } else cimg_forXY(*this,x,y) { const T val = (*this)(x,y,k); const int X = x*bx/width, Y = y*by/height, R = (int)((val-m)*br/range); const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1); (*this)(x,y,k) = (T)(bval0/bval1); } } } } return *this; } //! Blur an image using the bilateral filter. CImg get_blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, const bool interpolation=true) const { return (+*this).blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation); } //! In-place version of the previous function. CImg& blur_bilateral(const float sigmas, const float sigmar, const int bgrids=-33, const int bgridr=32, const bool interpolation=true) { return blur_bilateral(sigmas,sigmas,sigmas,sigmar,bgrids,bgrids,bgrids,bgridr,interpolation); } //! Return the Fast Fourier Transform of an image (along a specified axis) CImgList::type> get_FFT(const char axe, const bool inverse=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).FFT(axe,inverse); } //! Return the Fast Fourier Transform on an image CImgList::type> get_FFT(const bool inverse=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).FFT(inverse); } //! Apply a median filter. CImg get_blur_median(const unsigned int n=3) { CImg res(width,height,depth,dim); if (!n || n==1) return *this; const int hl=n/2, hr=hl-1+n%2; if (res.depth!=1) // 3D median filter { CImg vois; cimg_forXYZV(*this,x,y,z,k) { const int x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0, nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1, nz1 = z1>=dimz()?dimz()-1:z1; vois = get_crop(nx0,ny0,nz0,k,nx1,ny1,nz1,k); res(x,y,z,k) = vois.median(); } } else { #define _median_sort(a,b) if ((a)>(b)) cimg::swap(a,b) if (res.height!=1) switch (n) // 2D median filter { case 3: { T I[9] = { 0 }; CImg_3x3(J,T); cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { std::memcpy(J,I,9*sizeof(T)); _median_sort(Jcp, Jnp); _median_sort(Jcc, Jnc); _median_sort(Jcn, Jnn); _median_sort(Jpp, Jcp); _median_sort(Jpc, Jcc); _median_sort(Jpn, Jcn); _median_sort(Jcp, Jnp); _median_sort(Jcc, Jnc); _median_sort(Jcn, Jnn); _median_sort(Jpp, Jpc); _median_sort(Jnc, Jnn); _median_sort(Jcc, Jcn); _median_sort(Jpc, Jpn); _median_sort(Jcp, Jcc); _median_sort(Jnp, Jnc); _median_sort(Jcc, Jcn); _median_sort(Jcc, Jnp); _median_sort(Jpn, Jcc); _median_sort(Jcc, Jnp); res(x,y,0,k) = Jcc; } } break; case 5: { T I[25] = { 0 }; CImg_5x5(J,T); cimg_forV(*this,k) cimg_for5x5(*this,x,y,0,k,I) { std::memcpy(J,I,25*sizeof(T)); _median_sort(Jbb, Jpb); _median_sort(Jnb, Jab); _median_sort(Jcb, Jab); _median_sort(Jcb, Jnb); _median_sort(Jpp, Jcp); _median_sort(Jbp, Jcp); _median_sort(Jbp, Jpp); _median_sort(Jap, Jbc); _median_sort(Jnp, Jbc); _median_sort(Jnp, Jap); _median_sort(Jcc, Jnc); _median_sort(Jpc, Jnc); _median_sort(Jpc, Jcc); _median_sort(Jbn, Jpn); _median_sort(Jac, Jpn); _median_sort(Jac, Jbn); _median_sort(Jnn, Jan); _median_sort(Jcn, Jan); _median_sort(Jcn, Jnn); _median_sort(Jpa, Jca); _median_sort(Jba, Jca); _median_sort(Jba, Jpa); _median_sort(Jna, Jaa); _median_sort(Jcb, Jbp); _median_sort(Jnb, Jpp); _median_sort(Jbb, Jpp); _median_sort(Jbb, Jnb); _median_sort(Jab, Jcp); _median_sort(Jpb, Jcp); _median_sort(Jpb, Jab); _median_sort(Jpc, Jac); _median_sort(Jnp, Jac); _median_sort(Jnp, Jpc); _median_sort(Jcc, Jbn); _median_sort(Jap, Jbn); _median_sort(Jap, Jcc); _median_sort(Jnc, Jpn); _median_sort(Jbc, Jpn); _median_sort(Jbc, Jnc); _median_sort(Jba, Jna); _median_sort(Jcn, Jna); _median_sort(Jcn, Jba); _median_sort(Jpa, Jaa); _median_sort(Jnn, Jaa); _median_sort(Jnn, Jpa); _median_sort(Jan, Jca); _median_sort(Jnp, Jcn); _median_sort(Jap, Jnn); _median_sort(Jbb, Jnn); _median_sort(Jbb, Jap); _median_sort(Jbc, Jan); _median_sort(Jpb, Jan); _median_sort(Jpb, Jbc); _median_sort(Jpc, Jba); _median_sort(Jcb, Jba); _median_sort(Jcb, Jpc); _median_sort(Jcc, Jpa); _median_sort(Jnb, Jpa); _median_sort(Jnb, Jcc); _median_sort(Jnc, Jca); _median_sort(Jab, Jca); _median_sort(Jab, Jnc); _median_sort(Jac, Jna); _median_sort(Jbp, Jna); _median_sort(Jbp, Jac); _median_sort(Jbn, Jaa); _median_sort(Jpp, Jaa); _median_sort(Jpp, Jbn); _median_sort(Jcp, Jpn); _median_sort(Jcp, Jan); _median_sort(Jnc, Jpa); _median_sort(Jbn, Jna); _median_sort(Jcp, Jnc); _median_sort(Jcp, Jbn); _median_sort(Jpb, Jap); _median_sort(Jnb, Jpc); _median_sort(Jbp, Jcn); _median_sort(Jpc, Jcn); _median_sort(Jap, Jcn); _median_sort(Jab, Jbc); _median_sort(Jpp, Jcc); _median_sort(Jcp, Jac); _median_sort(Jab, Jpp); _median_sort(Jab, Jcp); _median_sort(Jcc, Jac); _median_sort(Jbc, Jac); _median_sort(Jpp, Jcp); _median_sort(Jbc, Jcc); _median_sort(Jpp, Jbc); _median_sort(Jpp, Jcn); _median_sort(Jcc, Jcn); _median_sort(Jcp, Jcn); _median_sort(Jcp, Jbc); _median_sort(Jcc, Jnn); _median_sort(Jcp, Jcc); _median_sort(Jbc, Jnn); _median_sort(Jcc, Jba); _median_sort(Jbc, Jba); _median_sort(Jbc, Jcc); res(x,y,0,k) = Jcc; } } break; default: { CImg vois; cimg_forXYV(*this,x,y,k) { const int x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr, nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nx1 = x1>=dimx()?dimx()-1:x1, ny1 = y1>=dimy()?dimy()-1:y1; vois = get_crop(nx0,ny0,0,k,nx1,ny1,0,k); res(x,y,0,k) = vois.median(); } } break; } else switch (n) // 1D median filter { case 2: { T I[4] = { 0 }; cimg_forV(*this,k) cimg_for2x2(*this,x,y,0,k,I) res(x,0,0,k) = (T)(0.5f*(I[0]+I[1])); } break; case 3: { T I[9] = { 0 }; cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { res(x,0,0,k) = I[3] vois; cimg_forXV(*this,x,k) { const int x0 = x - hl, x1 = x + hr, nx0 = x0<0?0:x0, nx1 = x1>=dimx()?dimx()-1:x1; vois = get_crop(nx0,0,0,k,nx1,0,0,k); res(x,0,0,k) = vois.median(); } } break; } } return res; } //! In-place version of the previous function. CImg& blur_median(const unsigned int n=3) { return get_blur_median(n).assign_to(*this); } //! Sharpen image using anisotropic shock filters CImg get_sharpen(const float amplitude=50.0f, const float edge=1.0f, const float alpha=0.0f, const float sigma=0.0f) const { return (+*this).sharpen(amplitude,edge,alpha,sigma); } //! In-place version of the previous function. CImg& sharpen(const float amplitude=50.0f, const float edge=1.0f, const float alpha=0.0f, const float sigma=0.0f) { if (is_empty()) return *this; const bool threed = (depth>1); const float nedge = 0.5f*edge; typedef typename cimg::superset::type ftype; CImg val, vec, veloc(width,height,depth,dim); if (threed) { CImg G = (alpha>0?get_blur(alpha).get_structure_tensorXYZ():get_structure_tensorXYZ()); if (sigma>0) G.blur(sigma); CImg_3x3x3(I,float); cimg_forXYZ(G,x,y,z) { G.get_tensor_at(x,y,z).symmetric_eigen(val,vec); G(x,y,z,0) = vec(0,0); G(x,y,z,1) = vec(0,1); G(x,y,z,2) = vec(0,2); G(x,y,z,3) = 1.0f-(float)std::pow((float)(1+val[0]+val[1]+val[2]),-nedge); } cimg_forV(*this,k) cimg_for3x3x3(*this,x,y,z,k,I) { const float u = G(x,y,z,0), v = G(x,y,z,1), w = G(x,y,z,2), amp = G(x,y,z,3), ixx = Incc+Ipcc-2*Iccc, ixy = 0.25f*(Innc+Ippc-Inpc-Ipnc), ixz = 0.25f*(Incn+Ipcp-Incp-Ipcn), iyy = Icnc+Icpc-2*Iccc, iyz = 0.25f*(Icnn+Icpp-Icnp-Icpn), izz = Iccn+Iccp-2*Iccc, ixf = Incc-Iccc, ixb = Iccc-Ipcc, iyf = Icnc-Iccc, iyb = Iccc-Icpc, izf = Iccn-Iccc, izb = Iccc-Iccp, itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz, it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb); veloc(x,y,z,k) = -amp*cimg::sign(itt)*cimg::abs(it); } } else { CImg G = (alpha>0?get_blur(alpha).get_structure_tensorXY():get_structure_tensorXY()); if (sigma>0) G.blur(sigma); CImg_3x3(I,float); cimg_forXY(G,x,y) { G.get_tensor_at(x,y).symmetric_eigen(val,vec); G(x,y,0) = vec(0,0); G(x,y,1) = vec(0,1); G(x,y,2) = 1.0f-(float)std::pow((float)(1+val[0]+val[1]),-nedge); } cimg_forV(*this,k) cimg_for3x3(*this,x,y,0,k,I) { const float u = G(x,y,0), v = G(x,y,1), amp = G(x,y,2), ixx = Inc+Ipc-2*Icc, ixy = 0.25f*(Inn+Ipp-Inp-Ipn), iyy = Icn+Icp-2*Icc, ixf = Inc-Icc, ixb = Icc-Ipc, iyf = Icn-Icc, iyb = Icc-Icp, itt = u*u*ixx + v*v*iyy + 2*u*v*ixy, it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb); veloc(x,y,k) = -amp*cimg::sign(itt)*cimg::abs(it); } } float m, M = (float)veloc.maxmin(m); const float vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M)); if (vmax!=0) { veloc*=amplitude/vmax; (*this)+=veloc; } return *this; } //! Compute the Haar multiscale wavelet transform (monodimensional version). /** \param axis Axis considered for the transform. \param inverse Set inverse of direct transform. \param nb_scales Number of scales used for the transform. **/ CImg::type> get_haar(const char axis, const bool inverse=false, const unsigned int nb_scales=1) const { if (is_empty() || !nb_scales) return *this; typedef typename cimg::superset::type ftype; CImg res; if (nb_scales==1) { switch (cimg::uncase(axis)) // Single scale transform { case 'x': { const unsigned int w = width/2; if (w) { if (w%2) throw CImgInstanceException("CImg<%s>::haar() : Sub-image width = %u is not even at a particular scale (=%u).",pixel_type(),w); res.assign(width,height,depth,dim); if (inverse) cimg_forYZV(*this,y,z,v) // Inverse transform along X { for (unsigned int x=0, xw=w, x2=0; x::haar() : Sub-image height = %u is not even at a particular scale.",pixel_type(),h); res.assign(width,height,depth,dim); if (inverse) cimg_forXZV(*this,x,z,v) // Inverse transform along Y { for (unsigned int y=0, yh=h, y2=0; y::haar() : Sub-image depth = %u is not even at a particular scale.",pixel_type(),d); res.assign(width,height,depth,dim); if (inverse) cimg_forXYV(*this,x,y,v) // Inverse transform along Z { for (unsigned int z=0, zd=d, z2=0; z::haar() : Unknown axis '%c'.",pixel_type(),axis); break; } } else // Multi-scale version { if (inverse) { res.assign(*this); switch (cimg::uncase(axis)) { case 'x': { unsigned int w = width; for (unsigned int s=1; w && s::haar() : Unknown axis '%c'.",pixel_type(),axis); break; } } else // Direct transform { res = get_haar(axis,false,1); switch (cimg::uncase(axis)) { case 'x': { for (unsigned int s=1, w=width/2; w && s::haar() : Unknown axis '%c'.",pixel_type(),axis); break; } } } return res; } //! In-place version of the previous function. CImg& haar(const char axis, const bool inverse=false, const unsigned int nb_scales=1) { return get_haar(axis,inverse,nb_scales).assign_to(*this); } //! Compute the Haar multiscale wavelet transform. /** \param inverse Set inverse of direct transform. \param nb_scales Number of scales used for the transform. **/ CImg::type> get_haar(const bool inverse=false, const unsigned int nb_scales=1) const { typedef typename cimg::superset::type ftype; CImg res; if (nb_scales==1) // Single scale transform { if (width>1) get_haar('x',inverse,1).assign_to(res); if (height>1) { if (res) res.get_haar('y',inverse,1).assign_to(res); else get_haar('y',inverse,1).assign_to(res); } if (depth>1) { if (res) res.get_haar('z',inverse,1).assign_to(res); else get_haar('z',inverse,1).assign_to(res); } if (res) return res; } else // Multi-scale transform { if (inverse) // Inverse transform { res.assign(*this); if (width>1) { if (height>1) { if (depth>1) { unsigned int w = width, h = height, d = depth; for (unsigned int s=1; w && h && d && s1) { unsigned int w = width, d = depth; for (unsigned int s=1; w && d && s1) { if (depth>1) { unsigned int h = height, d = depth; for (unsigned int s=1; h && d && s1) { unsigned int d = depth; for (unsigned int s=1; d && s1) { if (height>1) { if (depth>1) for (unsigned int s=1, w=width/2, h=height/2, d=depth/2; w && h && d && s1) for (unsigned int s=1, w=width/2, d=depth/2; w && d && s1) { if (depth>1) for (unsigned int s=1, h=height/2, d=depth/2; h && d && s1) for (unsigned int s=1, d=depth/2; d && s::type> get_displacement_field(const CImg& reference, const float smooth=0.1f, const float precision=1e-6f, const unsigned int nb_scale=0, const unsigned int itermax=10000) const { typedef typename cimg::superset::type ftype; if (is_empty() || !reference) return *this; if (!is_sameXYZ(reference)) throw CImgArgumentException("CImg<%s>::get_displacement_field() : Instance image (%u,%u,%u,%u,%p) and reference image (%u,%u,%u,%u,%p) " "must have same dimensions", pixel_type(),width,height,depth,dim,data, reference.width,reference.height,reference.depth,reference.dim,reference.data); const bool threed = depth>1; CImg u(width,height,depth,threed?3:2,0); const unsigned int nbs = nb_scale>0?nb_scale:(unsigned int)(2*std::log((double)(cimg::max(width,height,depth)))); for (int scale=nbs-1; scale>=0; --scale) { const float fact = (float)std::pow(1.5f,-(float)scale); const unsigned int tnw = (unsigned int)(width*fact), nw = tnw?tnw:1, tnh = (unsigned int)(height*fact), nh = tnh?tnh:1, tnd = (unsigned int)(depth*fact), nd = tnd?tnd:1; CImg I1 = get_resize(nw,nh,nd,1,3), I2 = reference.get_resize(nw,nh,nd,1,3); I1.norm_pointwise(1); I2.norm_pointwise(2); float m1, M1 = (float)I1.maxmin(m1), m2, M2 = (float)I2.maxmin(m2); const float M = cimg::max(cimg::abs(m1),cimg::abs(M1),cimg::abs(m2),cimg::abs(M2)); I1/=M; I2/=M; u*=1.5; u.resize(nw,nh,nd,threed?3:2,3); float dt = 100, E = cimg::type::max(), nprecision = nw*nh*nd*precision; const CImgList dI = threed?I2.get_gradientXYZ():I2.get_gradientXY(); for (unsigned int iter=0; iter& reference, const float smooth=0.1f, const float precision=1e-6f, const unsigned int nb_scale=0, const unsigned int itermax=10000) { return get_displacement_field(reference,smooth,precision,nb_scale,itermax).assign_to(*this); } //@} //----------------------------- // //! \name Matrix and Vectors //@{ //----------------------------- //! Return a vector with specified coefficients static CImg vector(const T& a0) { static CImg r(1,1); r[0] = a0; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1) { static CImg r(1,2); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2) { static CImg r(1,3); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3) { static CImg r(1,4); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { static CImg r(1,5); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) { static CImg r(1,6); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { static CImg r(1,7); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7) { static CImg r(1,8); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8) { static CImg r(1,9); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9) { static CImg r(1,10); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10) { static CImg r(1,11); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11) { static CImg r(1,12); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12) { static CImg r(1,13); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12, const T& a13) { static CImg r(1,14); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12, const T& a13, const T& a14) { static CImg r(1,15); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; return r; } //! Return a vector with specified coefficients static CImg vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, const T& a15) { static CImg r(1,16); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; return r; } //! Return a vector with specified coefficients template static CImg vector(const int a0, const int a1, ...) { CImg res(1,N); _CImg_stdarg(res,a0,a1,N,int); return res; } //! Return a vector with specified coefficients template static CImg vector(const double a0, const double a1, ...) { CImg res(1,N); _CImg_stdarg(res,a0,a1,N,double); return res; } //! Return a 1x1 square matrix with specified coefficients static CImg matrix(const T& a0) { return vector(a0); } //! Return a 2x2 square matrix with specified coefficients static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3) { static CImg r(2,2); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; return r; } //! Return a 3x3 square matrix with specified coefficients static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8) { static CImg r(3,3); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; return r; } //! Return a 4x4 square matrix with specified coefficients static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, const T& a15) { static CImg r(4,4); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; return r; } //! Return a 5x5 square matrix with specified coefficients static CImg matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6, const T& a7, const T& a8, const T& a9, const T& a10, const T& a11, const T& a12, const T& a13, const T& a14, const T& a15, const T& a16, const T& a17, const T& a18, const T& a19, const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) { static CImg r(5,5); T *ptr = r.data; *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19; *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24; return r; } //! Return a MxN square matrix with specified coefficients template static CImg matrix(const int a0, const int a1, ...) { CImg res(M,N); _CImg_stdarg(res,a0,a1,M*N,int); return res; } //! Return a NxN square matrix with specified coefficients template static CImg matrix(const double a0, const double a1, ...) { CImg res(M,N); _CImg_stdarg(res,a0,a1,M*N,double); return res; } //! Return a 1x1 symmetric matrix with specified coefficients static CImg tensor(const T& a1) { return matrix(a1); } //! Return a 2x2 symmetric matrix tensor with specified coefficients static CImg tensor(const T& a1, const T& a2, const T& a3) { return matrix(a1,a2,a2,a3); } //! Return a 3x3 symmetric matrix with specified coefficients static CImg tensor(const T& a1, const T& a2, const T& a3, const T& a4, const T& a5, const T& a6) { return matrix(a1,a2,a3,a2,a4,a5,a3,a5,a6); } //! Return a 1x1 diagonal matrix with specified coefficients static CImg diagonal(const T& a0) { return matrix(a0); } //! Return a 2x2 diagonal matrix with specified coefficients static CImg diagonal(const T& a0, const T& a1) { return matrix(a0,0,0,a1); } //! Return a 3x3 diagonal matrix with specified coefficients static CImg diagonal(const T& a0, const T& a1, const T& a2) { return matrix(a0,0,0,0,a1,0,0,0,a2); } //! Return a 4x4 diagonal matrix with specified coefficients static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3) { return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3); } //! Return a 5x5 diagonal matrix with specified coefficients static CImg diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) { return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4); } //! Return a NxN diagonal matrix with specified coefficients template static CImg diagonal(const int a0, ...) { CImg res; if (N>0) { res.assign(N,N,1,1,0); va_list ap; va_start(ap,a0); res[0] = (T)a0; for (int i=1; i static CImg diagonal(const double a0, ...) { CImg res; if (N>0) { res.assign(N,N,1,1,0); va_list ap; va_start(ap,a0); res[0] = (T)a0; for (int i=1; i res(N,N,1,1,0); cimg_forX(res,x) res(x,x) = 1; return res; } //! Return a N-numbered sequence vector from \p a0 to \p a1 static CImg sequence(const unsigned int N, const T& a0, const T& a1) { if (N) return CImg(1,N).sequence(a0,a1); return CImg(); } //! Return a 3x3 rotation matrix along the (x,y,z)-axis with an angle w. static CImg rotation_matrix(const float x, const float y, const float z, const float w, const bool quaternion_data=false) { float X,Y,Z,W; if (!quaternion_data) { const float norm = (float)std::sqrt(x*x + y*y + z*z), nx = norm>0?x/norm:0, ny = norm>0?y/norm:0, nz = norm>0?z/norm:1, nw = norm>0?w:0, sina = (float)std::sin(nw/2), cosa = (float)std::cos(nw/2); X = nx*sina; Y = ny*sina; Z = nz*sina; W = cosa; } else { const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w); if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; } else { X = Y = Z = 0; W = 1; } } const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W; return CImg::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)), (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)), (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy))); } //! Return a new image corresponding to the vector located at (\p x,\p y,\p z) of the current vector-valued image. CImg get_vector_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const { static CImg dest; if (dest.height!=dim) dest.assign(1,dim); const unsigned int whz = width*height*depth; const T *ptrs = ptr(x,y,z); T *ptrd = dest.data; cimg_forV(*this,k) { *(ptrd++) = *ptrs; ptrs+=whz; } return dest; } //! Set the image \p vec as the \a vector \a valued pixel located at (\p x,\p y,\p z) of the current vector-valued image. template CImg& set_vector_at(const CImg& vec, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) { if (x::matrix() : Image size = %u is not a square number",pixel_type(),siz); } break; } return *this; } //! Realign pixel values of the instance image as a symmetric tensor. CImg get_tensor() const { CImg res; const unsigned int siz = size(); switch (siz) { case 1: break; case 3: res.assign(2,2); res(0,0) = (*this)(0); res(1,0) = res(0,1) = (*this)(1); res(1,1) = (*this)(2); break; case 6: res.assign(3,3); res(0,0) = (*this)(0); res(1,0) = res(0,1) = (*this)(1); res(2,0) = res(0,2) = (*this)(2); res(1,1) = (*this)(3); res(2,1) = res(1,2) = (*this)(4); res(2,2) = (*this)(5); break; default: throw CImgInstanceException("CImg<%s>::tensor() : Wrong vector dimension = %u in instance image.", pixel_type(), dim); break; } return res; } //! In-place version of the previous function. CImg& tensor() { return get_tensor().assign_to(*this); } //! Unroll all images values into specified axis. CImg get_unroll(const char axe='x') const { return (+*this).unroll(axe); } //! In-place version of the previous function. CImg& unroll(const char axe='x') { const unsigned int siz = size(); if (siz) switch (axe) { case 'x': width = siz; height=depth=dim=1; break; case 'y': height = siz; width=depth=dim=1; break; case 'z': depth = siz; width=height=dim=1; break; case 'v': dim = siz; width=height=depth=1; break; default: throw CImgArgumentException("CImg<%s>::unroll() : Given axe is '%c' which is not 'x','y','z' or 'v'", pixel_type(),axe); } return *this; } //! Get a diagonal matrix, whose diagonal coefficients are the coefficients of the input image CImg get_diagonal() const { if (is_empty()) return CImg(); CImg res(size(),size(),1,1,0); cimg_foroff(*this,off) res(off,off) = (*this)(off); return res; } //! In-place version of the previous function. CImg& diagonal() { return get_diagonal().assign_to(*this); } //! Get an identity matrix having same dimension than instance image. CImg get_identity_matrix() const { return identity_matrix(cimg::max(width,height)); } //! In-place version of the previous function. CImg& identity_matrix() { return identity_matrix(cimg::max(width,height)).assign_to(*this); } //! Return a N-numbered sequence vector from \p a0 to \p a1 CImg get_sequence(const T& a0, const T& a1) const { return (+*this).sequence(a0,a1); } //! In-place version of the previous function. CImg& sequence(const T& a0, const T& a1) { if (!is_empty()) { const unsigned int siz = size()-1; const float delta = (float)((float)a1-a0); T* ptr = data; cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz); } return *this; } //! Return the transpose version of the current matrix. CImg get_transpose() const { return get_permute_axes("yxzv"); } //! In-place version of the previous function. CImg& transpose() { if (width==1) { width=height; height=1; return *this; } if (height==1) { height=width; width=1; return *this; } if (width==height) { cimg_forYZV(*this,y,z,v) for (int x=y; x::type> get_inverse(const bool use_LU=true) const { typedef typename cimg::superset::type restype; return CImg(*this,false).inverse(use_LU); } //! In-place version of the previous function. CImg& inverse(const bool use_LU=true) { if (!is_empty()) { if (width!=height || depth!=1 || dim!=1) throw CImgInstanceException("CImg<%s>::inverse() : Instance matrix (%u,%u,%u,%u,%p) is not square.", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_lapack typedef typename cimg::superset::type ftype; int INFO = (int)use_LU, N = width, LWORK = 4*N, *IPIV = new int[N]; ftype *lapA = new ftype[N*N], *WORK = new ftype[LWORK]; cimg_forXY(*this,k,l) lapA[k*N+l] = (ftype)((*this)(k,l)); cimg::getrf(N,lapA,IPIV,INFO); if (INFO) cimg::warn("CImg<%s>::inverse() : LAPACK library function dgetrf_() returned error code %d.",pixel_type(),INFO); else { cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO); if (INFO) cimg::warn("CImg<%s>::inverse() : LAPACK library function dgetri_() returned Error code %d",pixel_type(),INFO); } if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0); delete[] IPIV; delete[] lapA; delete[] WORK; #else const double dete = width>3?-1.0:det(); if (dete!=0.0 && width==2) { const double a = data[0], c = data[1], b = data[2], d = data[3]; data[0] = (T)(d/dete); data[1] = (T)(-c/dete); data[2] = (T)(-b/dete); data[3] = (T)(a/dete); } else if (dete!=0.0 && width==3) { const double a = data[0], d = data[1], g = data[2], b = data[3], e = data[4], h = data[5], c = data[6], f = data[7], i = data[8]; data[0] = (T)((i*e-f*h)/dete), data[1] = (T)((g*f-i*d)/dete), data[2] = (T)((d*h-g*e)/dete); data[3] = (T)((h*c-i*b)/dete), data[4] = (T)((i*a-c*g)/dete), data[5] = (T)((g*b-a*h)/dete); data[6] = (T)((b*f-e*c)/dete), data[7] = (T)((d*c-a*f)/dete), data[8] = (T)((a*e-d*b)/dete); } else { typedef typename cimg::superset::type ftype; if (use_LU) // LU-based inverse computation { CImg A(*this), indx, col(1,width); bool d; A._LU(indx,d); cimg_forX(*this,j) { col.fill(0); col(j) = 1; col._solve(A,indx); cimg_forX(*this,i) (*this)(j,i) = (T)col(i); } } else // SVD-based inverse computation { CImg U(width,width), S(1,width), V(width,width); SVD(U,S,V,false); U.transpose(); cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k]; S.diagonal(); *this = V*S*U; } } #endif } return *this; } //! Return the pseudo-inverse (Moore-Penrose) of the matrix CImg::type> get_pseudoinverse() const { typedef typename cimg::superset::type restype; CImg U, S, V; SVD(U,S,V); cimg_forX(V,x) { const float s = S(x), invs = s!=0.0f?1.0f/s:0.0f; cimg_forY(V,y) V(x,y)*=invs; } return V*U.transpose(); } //! In-place version of the previous function. CImg& pseudoinverse() { return get_pseudoinverse().assign_to(*this); } //! Compute the cross product between two 3d vectors. template CImg::type> get_cross(const CImg& img) const { typedef typename cimg::superset::type restype; return CImg(*this).cross(img); } //! In-place version of the previous function. template CImg& cross(const CImg& img) { if (width!=1 || height<3 || img.width!=1 || img.height<3) throw CImgInstanceException("CImg<%s>::cross() : Arguments (%u,%u,%u,%u,%p) and (%u,%u,%u,%u,%p) must be both 3d vectors.", pixel_type(),width,height,depth,dim,data,img.width,img.height,img.depth,img.dim,img.data); const T x = (*this)[0], y = (*this)[1], z = (*this)[2]; (*this)[0] = (T)(y*img[2]-z*img[1]); (*this)[1] = (T)(z*img[0]-x*img[2]); (*this)[2] = (T)(x*img[1]-y*img[0]); return *this; } //! Solve a linear system AX=B where B=*this. template CImg::type> get_solve(const CImg& A) const { typedef typename cimg::superset2::type restype; return CImg(*this,false).solve(A); } //! In-place version of the previous function. template CImg& solve(const CImg& A) { if (width!=1 || depth!=1 || dim!=1 || height!=A.height || A.depth!=1 || A.dim!=1) throw CImgArgumentException("CImg<%s>::solve() : Instance matrix size is (%u,%u,%u,%u) while " "size of given matrix A is (%u,%u,%u,%u).", pixel_type(),width,height,depth,dim,A.width,A.height,A.depth,A.dim); typedef typename cimg::superset2::type ftype; if (A.width==A.height) { #ifdef cimg_use_lapack char TRANS='N'; int INFO, N = height, LWORK = 4*N, one = 1, *IPIV = new int[N]; ftype *lapA = new ftype[N*N], *lapB = new ftype[N], *WORK = new ftype[LWORK]; cimg_forXY(A,k,l) lapA[k*N+l] = (ftype)(A(k,l)); cimg_forY(*this,i) lapB[i] = (ftype)((*this)(i)); cimg::getrf(N,lapA,IPIV,INFO); if (INFO) cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrf_() returned error code %d.",pixel_type(),INFO); if (!INFO) { cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO); if (INFO) cimg::warn("CImg<%s>::solve() : LAPACK library function dgetrs_() returned Error code %d",pixel_type(),INFO); } if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0); delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK; #else CImg lu(A); CImg indx; bool d; lu._LU(indx,d); _solve(lu,indx); #endif } else assign(A.get_pseudoinverse()*(*this)); return *this; } template CImg& _solve(const CImg& A, const CImg& indx) { typedef typename cimg::superset2::type ftype; const int N = size(); int ii = -1; ftype sum; for (int i=0; i=0) for (int j=ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j); else if (sum!=0) ii=i; (*this)(i) = (T)sum; } { for (int i=N-1; i>=0; --i) { sum = (*this)(i); for (int j=i+1; j CImg get_sort(CImg& permutations, const bool increasing=true) const { return (+*this).sort(permutations,increasing); } //! In-place version of the previous function. template CImg& sort(CImg& permutations, const bool increasing=true) { if (is_empty()) permutations.assign(); else { if (permutations.size()!=size()) permutations.assign(size()); cimg_foroff(permutations,off) permutations[off] = (t)off; _quicksort(0,size()-1,permutations,increasing); } return *this; } // Sort image values CImg get_sort(const bool increasing=true) const { return (+*this).sort(increasing); } //! In-place version of the previous function. CImg& sort(const bool increasing=true) { CImg foo; return sort(foo,increasing); } template CImg& _quicksort(const int min, const int max, CImg& permutations, const bool increasing) { if (min(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } if ((*this)[mid]>(*this)[max]) { cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } if ((*this)[min]>(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } } else { if ((*this)[min]<(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } if ((*this)[mid]<(*this)[max]) { cimg::swap((*this)[max],(*this)[mid]); cimg::swap(permutations[max],permutations[mid]); } if ((*this)[min]<(*this)[mid]) { cimg::swap((*this)[min],(*this)[mid]); cimg::swap(permutations[min],permutations[mid]); } } if (max-min>=3) { const T pivot = (*this)[mid]; int i = min, j = max; if (increasing) { do { while ((*this)[i]pivot) --j; if (i<=j) { cimg::swap((*this)[i],(*this)[j]); cimg::swap(permutations[i++],permutations[j--]); } } while (i<=j); } else { do { while ((*this)[i]>pivot) ++i; while ((*this)[j] CImg get_permute(const CImg& permutation) const { if (permutation.size()!=size()) throw CImgArgumentException("CImg<%s>::permute() : Instance image (%u,%u,%u,%u,%p) and permutation (%u,%u,%u,%u,%p)" "have different sizes.",pixel_type(), width,height,depth,dim,data, permutation.width,permutation.height,permutation.depth,permutation.dim,permutation.data); CImg res(width,height,depth,dim); const t *p = permutation.ptr(permutation.size()); cimg_for(res,ptr,T) *ptr = (*this)[*(--p)]; return res; } //! In-place version of the previous function. template CImg& permute(const CImg& permutation) { return get_permute(permutation).assign_to(*this); } //! Compute the SVD of a general matrix. template const CImg& SVD(CImg& U, CImg& S, CImg& V, const bool sorting=true, const unsigned int max_iter=40, const float lambda=0) const { if (is_empty()) { U.assign(); S.assign(); V.assign(); } else { #ifdef cimg_use_lapack typedef typename cimg::superset::type ftype; int M = height, N = width, mn = cimg::min(M,N), MN = cimg::max(M,N), LWORK = 4*mn+2*MN, INFO; char JOB='A'; ftype *lapA = new ftype[M*N], *lapS = new ftype[mn], *lapU = new ftype[M*M], *lapV = new ftype[N*N], *WORK = new ftype[LWORK]; cimg_forXY(*this,k,l) lapA[k*N+l] = (ftype)((*this)(k,l)); cimg::gesvd(JOB,M,N,lapA,MN,lapS,lapU,lapV,WORK,LWORK,INFO); if (INFO) cimg::warn("CImg<%s>::SVD() : LAPACK library function gesvd_() returned error code %d.",pixel_type(),INFO); U.assign(M,M); S.assign(1,mn); V.assign(N,N); if (!INFO) { cimg_forY(S,i) S(i) = (T)(lapS[i]); cimg_forXY(U,p,q) U(p,q) = (T)(lapU[p*N+q]); cimg_forXY(V,k,l) V(l,k) = (T)(lapV[k*N+l]); } else { S.fill(0); U.fill(0); V.fill(0); } delete[] lapA; delete[] lapS; delete[] lapU; delete[] lapV; delete[] WORK; #else U = *this; if (lambda!=0.0f) { const unsigned int delta = cimg::min(U.width,U.height); for (unsigned int i=0; i rv1(width); t anorm = 0, c, f, g = 0, h, s, scale = 0; int l = 0, nm = 0; cimg_forX(U,i) { l = i+1; rv1[i] = scale*g; g = s = scale = 0; if (i=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g; for (int j=l; j=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g; { for (int k=l; k=0; --i) { if (i=0; --i) { l = i+1; g = S[i]; for (int j=l; j=0; --k) { for (unsigned int its=0; its=1; --l) { nm = l-1; if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; } if ((cimg::abs(S[nm])+anorm)==anorm) break; } if (flag) { c = 0; s = 1; for (int i=l; i<=k; ++i) { f = s*rv1[i]; rv1[i] = c*rv1[i]; if ((cimg::abs(f)+anorm)==anorm) break; g = S[i]; h = (t)cimg::pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h; cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c+z*s; U(i,j) = z*c-y*s; } } } const t& z = S[k]; if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; } nm = k-1; t x = S[l], y = S[nm]; g = rv1[nm]; h = rv1[k]; f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y); g = (t)cimg::pythagore(f,1.0); f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x; c = s = 1; for (int j=l; j<=nm; ++j) { const int i = j+1; g = rv1[i]; h = s*g; g = c*g; t y = S[i]; t z = (t)cimg::pythagore(f,h); rv1[j] = z; c = f/z; s = h/z; f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c; cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c+z*s; V(i,jj) = z*c-x*s; } z = (t)cimg::pythagore(f,h); S[j] = z; if (z) { z = 1/z; c = f*z; s = h*z; } f = c*g+s*y; x = c*y-s*g; { cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c+z*s; U(i,jj) = z*c-y*s; } } } rv1[l] = 0; rv1[k]=f; S[k]=x; } } if (sorting) { CImg permutations(width); CImg tmp(width); S.sort(permutations,false); cimg_forY(U,k) { cimg_forX(permutations,x) tmp(x) = U(permutations(x),k); std::memcpy(U.ptr(0,k),tmp.data,sizeof(t)*width); } { cimg_forY(V,k) { cimg_forX(permutations,x) tmp(x) = V(permutations(x),k); std::memcpy(V.ptr(0,k),tmp.data,sizeof(t)*width); } } } #endif } return *this; } //! Compute the SVD of a general matrix. template const CImg& SVD(CImgList& USV) const { if (USV.size<3) USV.assign(3); return SVD(USV[0],USV[1],USV[2]); } //! Compute the SVD of a general matrix. CImgList::type> get_SVD(const bool sorting=true) const { typedef typename cimg::superset::type restype; CImgList res(3); SVD(res[0],res[1],res[2],sorting); return res; } // INNER ROUTINE : Compute the LU decomposition of a permuted matrix (c.f. numerical recipies) template CImg& _LU(CImg& indx, bool& d) { typedef typename cimg::superset::type ftype; const int N = dimx(); int imax = 0; CImg vv(N); indx.assign(N); d = true; cimg_forX(*this,i) { ftype vmax = 0; cimg_forX(*this,j) { const ftype tmp = cimg::abs((*this)(j,i)); if (tmp>vmax) vmax = tmp; } if (vmax==0) { indx.fill(0); return fill(0); } vv[i] = 1/vmax; } cimg_forX(*this,j) { for (int i=0; i=vmax) { vmax=tmp; imax=i; } } } if (j!=imax) { cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j)); d =!d; vv[imax] = vv[j]; } indx[j] = (t)imax; if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20; if (j::type> get_eigen() const { typedef typename cimg::superset::type restype; CImgList res(2); eigen(res[0],res[1]); return res; } //! Compute the eigenvalues and eigenvectors of a matrix. template const CImg& eigen(CImg& val, CImg &vec) const { if (is_empty()) { val.assign(); vec.assign(); } else { if (width!=height || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (val.size()::eigen() : Complex eigenvalues",pixel_type()); f = std::sqrt(f); const double l1 = 0.5*(e-f), l2 = 0.5*(e+f); const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b); val[0]=(t)l2; val[1]=(t)l1; vec(0,0) = (t)std::cos(theta1); vec(0,1) = (t)std::sin(theta1); vec(1,0) = (t)std::cos(theta2); vec(1,1) = (t)std::sin(theta2); } break; default: throw CImgInstanceException("CImg<%s>::eigen() : Eigenvalues computation of general matrices is limited" "to 2x2 matrices (given is %ux%u)", pixel_type(),width,height); } } return *this; } //! Compute the eigenvalues and eigenvectors of a symmetric matrix. CImgList::type> get_symmetric_eigen() const { typedef typename cimg::superset::type restype; CImgList res(2); symmetric_eigen(res[0],res[1]); return res; } //! Compute the eigenvalues and eigenvectors of a symmetric matrix. template const CImg& symmetric_eigen(CImg& val, CImg& vec) const { if (is_empty()) { val.assign(); vec.assign(); } else { #ifdef cimg_use_lapack typedef typename cimg::superset::type ftype; char JOB = 'V', UPLO = 'U'; int N = width, LWORK = 4*N, INFO; ftype *lapA = new ftype[N*N], *lapW = new ftype[N], *WORK = new ftype[LWORK]; cimg_forXY(*this,k,l) lapA[k*N+l] = (ftype)((*this)(k,l)); cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO); if (INFO) cimg::warn("CImg<%s>::symmetric_eigen() : LAPACK library function dsyev_() returned error code %d.",pixel_type(),INFO); val.assign(1,N); vec.assign(N,N); if (!INFO) { cimg_forY(val,i) val(i) = (T)lapW[N-1-i]; cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]); } else { val.fill(0); vec.fill(0); } delete[] lapA; delete[] lapW; delete[] WORK; #else if (width!=height || depth>1 || dim>1) throw CImgInstanceException("CImg<%s>::eigen() : Instance object (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); if (val.size() V(width,width); SVD(vec,val,V,false); bool ambiguous = false; float eig = 0; cimg_forY(val,p) // check for ambiguous cases. { if (val[p]>eig) eig = val[p]; t scal = 0; cimg_forY(vec,y) scal+=vec(p,y)*V(p,y); if (cimg::abs(scal)<0.9f) ambiguous = true; if (scal<0) val[p] = -val[p]; } if (ambiguous) { (eig*=2)++; SVD(vec,val,V,false,40,eig); val-=eig; } CImg permutations(width); // sort eigenvalues in decreasing order CImg tmp(width); val.sort(permutations,false); cimg_forY(vec,k) { cimg_forX(permutations,x) tmp(x) = vec(permutations(x),k); std::memcpy(vec.ptr(0,k),tmp.data,sizeof(t)*width); } #endif } return *this; } //@} //------------------- // //! \name Display //@{ //------------------- //! Display an image into a CImgDisplay window. const CImg& display(CImgDisplay& disp) const { disp.display(*this); return *this; } //! Display an image in a window with a title \p title, and wait a 'is_closed' or 'keyboard' event.\n //! Parameters \p min_size and \p max_size set the minimum and maximum dimensions of the display window. //! If negative, they corresponds to a percentage of the original image size. const CImg& display(const char *const title, const int min_size=128, const int max_size=1024, const int print_flag=1) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::display() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); CImgDisplay disp; unsigned int w = width+(depth>1?depth:0), h = height+(depth>1?depth:0), XYZ[3]; print(title,print_flag); const unsigned int dmin = cimg::min(w,h), minsiz = min_size>=0?min_size:(-min_size)*dmin/100; if (dmin=0?max_size:(-max_size)*dmax/100; if (dmax>maxsiz) { w=w*maxsiz/dmax; w+=(w==0); h=h*maxsiz/dmax; h+=(h==0); } disp.assign(w,h,title,1,3); XYZ[0] = width/2; XYZ[1] = height/2; XYZ[2] = depth/2; while (!disp.is_closed && !disp.key) get_coordinates(1,disp,XYZ); return *this; } //! Display an image in a window, with a default title. See also \see display() for details on parameters. const CImg& display(const int min_size=128, const int max_size=1024, const int print_flag=1) const { char title[256] = { 0 }; std::sprintf(title,"CImg<%s>",pixel_type()); return display(title,min_size,max_size,print_flag); } //! Simple interface to select shaped from an image /** \param selection Array of 6 values containing the selection result \param coords_type Determine shape type to select (0=point, 1=vector, 2=rectangle, 3=circle) \param disp Display window used to make the selection \param XYZ Initial XYZ position (for volumetric images only) \param color Color of the shape selector. **/ CImg::type> get_coordinates(const int coords_type, CImgDisplay &disp, unsigned int *const XYZ=0, const unsigned char *const color=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::get_coordinates() : Instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data); const unsigned int old_events = disp.events, old_normalization = disp.normalization, hatch = 0x55555555; bool old_is_resized = disp.is_resized; disp.events = 3; disp.normalization = 0; disp.show().key = 0; unsigned char fgcolor[] = { 255,255,105 }, bgcolor[] = { 0,0,0 }; if (color) std::memcpy(fgcolor,color,sizeof(unsigned char)*cimg::min(3,dimv())); int area = 0, clicked_area = 0, phase = 0, X0 = (int)((XYZ?XYZ[0]:width/2)%width), Y0 = (int)((XYZ?XYZ[1]:height/2)%height), Z0 = (int)((XYZ?XYZ[2]:depth/2)%depth), X1 =-1, Y1 = -1, Z1 = -1, X = -1, Y = -1, Z = -1, oX = X, oY = Y, oZ = Z; unsigned int old_button = 0, key = 0; bool shape_selected = false, text_down = false; CImg visu, visu0; char text[1024] = { 0 }; while (!key && !disp.is_closed && !shape_selected) { // Handle mouse motion and selection oX = X; oY = Y; oZ = Z; int mx = disp.mouse_x, my = disp.mouse_y; const int mX = mx*(width+(depth>1?depth:0))/disp.width, mY = my*(height+(depth>1?depth:0))/disp.height; area = 0; if (mX=dimy()) { area = 2; X = mX; Z = mY-height; Y = phase?Y1:Y0; } if (mX>=dimx() && mY=2) { switch (clicked_area) { case 1: Z1 = Z; break; case 2: Y1 = Y; break; case 3: X1 = X; break; } } if (disp.button&2) { if (phase) { X1 = X; Y1 = Y; Z1 = Z; } else { X0 = X; Y0 = Y; Z0 = Z; } } if (disp.button&4) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu.assign(); } if (disp.wheel) { switch (area) { case 1: if (phase) Z = (Z1+=disp.wheel); else Z = (Z0+=disp.wheel); break; case 2: if (phase) Y = (Y1+=disp.wheel); else Y = (Y0+=disp.wheel); break; case 3: if (phase) X = (X1+=disp.wheel); else X = (X0+=disp.wheel); break; default: break; } disp.wheel = 0; } if ((disp.button&1)!=old_button) { switch (phase++) { case 0: X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; clicked_area = area; break; case 1: X1 = X; Y1 = Y; Z1 = Z; break; default: break; } old_button = disp.button&1; } if (depth>1 && (X!=oX || Y!=oY || Z!=oZ)) visu0.assign(); } if (phase) { if (!coords_type) shape_selected = phase?true:false; else { if (depth>1) shape_selected = (phase==3)?true:false; else shape_selected = (phase==2)?true:false; } } if (X0<0) X0 = 0; if (X0>=dimx()) X0 = dimx()-1; if (Y0<0) Y0 = 0; if (Y0>=dimy()) Y0 = dimy()-1; if (Z0<0) Z0 = 0; if (Z0>=dimz()) Z0 = dimz()-1; if (X1<1) X1 = 0; if (X1>=dimx()) X1 = dimx()-1; if (Y1<0) Y1 = 0; if (Y1>=dimy()) Y1 = dimy()-1; if (Z1<0) Z1 = 0; if (Z1>=dimz()) Z1 = dimz()-1; // Draw visualization image on the display if (oX!=X || oY!=Y || oZ!=Z || !visu0) { if (!visu0) { CImg tmp0 = get_shared_channels(0,cimg::min(2U,dim-1)), tmp; if (depth!=1) tmp = (!phase?tmp0.get_projections2d(X0,Y0,Z0):tmp0.get_projections2d(X1,Y1,Z1)).resize(disp.width,disp.height,1,3); else tmp = tmp0.get_resize(disp.width,disp.height,1,3); if (old_normalization) { if (old_normalization<3 || cimg::type::is_float()) { if (sizeof(T)>1) visu0.assign(tmp.normalize(0,255)); else visu0.assign(tmp).normalize(0,255); } else { if (cimg::type::string()!=cimg::type::string()) { const float m = (float)cimg::type::min(), M = (float)cimg::type::max(); visu0.assign((CImg(tmp)-=m)*=255.0f/(M-m)); } else visu0.assign(tmp); } } else visu0.assign(tmp); } visu = visu0; if (!color) { if (visu.mean()<200) { fgcolor[0] = fgcolor[1] = fgcolor[2] = 255; bgcolor[0] = bgcolor[1] = bgcolor[2] = 0; } else { fgcolor[0] = fgcolor[1] = fgcolor[2] = 0; bgcolor[0] = bgcolor[1] = bgcolor[2] = 255; } } const int d=(depth>1)?depth:0; if (phase) switch (coords_type) { case 1: { const int x0=(int)((X0+0.5f)*disp.width/(width+d)), y0=(int)((Y0+0.5f)*disp.height/(height+d)), x1=(int)((X1+0.5f)*disp.width/(width+d)), y1=(int)((Y1+0.5f)*disp.height/(height+d)); visu.draw_arrow(x0,y0,x1,y1,fgcolor,30.0f,5.0f,1.0f,hatch); if (d) { const int zx0=(int)((width+Z0+0.5f)*disp.width/(width+d)), zx1=(int)((width+Z1+0.5f)*disp.width/(width+d)), zy0=(int)((height+Z0+0.5f)*disp.height/(height+d)), zy1=(int)((height+Z1+0.5f)*disp.height/(height+d)); visu.draw_arrow(zx0,y0,zx1,y1,fgcolor,30.0f,5.0f,1.0f,hatch).draw_arrow(x0,zy0,x1,zy1,fgcolor,30.0f,5.0f,1.0f,hatch); } } break; case 2: { const int x0=(X0=visu.dimy()-11) text_down = false; if (!coords_type || !phase) { if (X>=0 && Y>=0 && Z>=0 && X1) std::sprintf(text,"Point (%d,%d,%d)={ ",X,Y,Z); else std::sprintf(text,"Point (%d,%d)={ ",X,Y); char *ctext = text + cimg::strlen(text), *const ltext = text + 512; for (unsigned int k=0; k::format(),cimg::type::format((*this)(X,Y,Z,k))); ctext = text + cimg::strlen(text); *(ctext++) = ' '; *ctext = '\0'; } std::sprintf(text + cimg::strlen(text),"}"); } } else switch (coords_type) { case 1: { const double dX=(double)(X0-X1), dY=(double)(Y0-Y1), dZ=(double)(Z0-Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ); if (depth>1) std::sprintf(text,"Vect (%d,%d,%d)-(%d,%d,%d), norm=%g",X0,Y0,Z0,X1,Y1,Z1,norm); else std::sprintf(text,"Vect (%d,%d)-(%d,%d), norm=%g",X0,Y0,X1,Y1,norm); } break; case 2: if (depth>1) std::sprintf(text,"Box (%d,%d,%d)-(%d,%d,%d), Size=(%d,%d,%d)", X01) std::sprintf(text,"Ellipse (%d,%d,%d)-(%d,%d,%d), Radii=(%d,%d,%d)", X0,Y0,Z0,X1,Y1,Z1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1)); else std::sprintf(text,"Ellipse (%d,%d)-(%d,%d), Radii=(%d,%d)", X0,Y0,X1,Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1)); break; } if (phase || (mx>=0 && my>=0)) visu.draw_text(text,0,text_down?visu.dimy()-11:0,fgcolor,bgcolor,11,0.7f); disp.display(visu).wait(25); } else disp.wait(); if (disp.is_resized) { disp.resize(false); old_is_resized = true; disp.is_resized = false; visu0.assign(); } } // Return result typedef typename cimg::last::type restype; CImg res(1,6,1,1,(restype)-1); if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; } if (shape_selected) { if (coords_type==2) { if (X0>X1) cimg::swap(X0,X1); if (Y0>Y1) cimg::swap(Y0,Y1); if (Z0>Z1) cimg::swap(Z0,Z1); } if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1; switch(coords_type) { case 1: case 2: res[3] = (restype)X1; res[4] = (restype)Y1; res[5] = (restype)Z1; default: res[0] = (restype)X0; res[1] = (restype)Y0; res[2] = (restype)Z0; } } disp.button = 0; disp.events = old_events; disp.normalization = old_normalization; disp.is_resized = old_is_resized; disp.key = key; return res; } //! High-level interface to select features in images CImg::type> get_coordinates(const int coords_type=0, unsigned int *const XYZ=0, const unsigned char *const color=0) const { unsigned int w = width + (depth>1?depth:0), h = height + (depth>1?depth:0); const unsigned int dmin = cimg::min(w,h), minsiz = 256; if (dminmaxsiz) { w=w*maxsiz/dmax; h=h*maxsiz/dmax; } CImgDisplay disp(w,h," ",1,3); return get_coordinates(coords_type,disp,XYZ,color); } CImg& coordinates(const int coords_type=0,unsigned int *const XYZ=0, const unsigned char *const color=0) { return get_coordinates(coords_type,XYZ,color).assign_to(*this); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const CImg& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, CImgDisplay& disp, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) const { // Check input arguments if (!points || !primitives || !opacities) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (%u), primitives (%u) or opacities (%u) are empty.", pixel_type(),points.size()/3,primitives.size,opacities.size); if (is_empty()) return CImg(disp.width,disp.height,1,colors[0].size(),0). display_object3d(points,primitives,colors,opacities,disp,centering, render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); if (points.height<3) return display_object3d(points.get_resize(-100,3,1,1,0),primitives,colors,opacities,disp, centering,render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); // Init 3D objects and compute object statistics CImg pose, rot_mat, centered_points = centering?CImg(points.width,3):CImg(), rotated_points(points.width,3), bbox_points, rotated_bbox_points, axes_points, rotated_axes_points; CImgList bbox_opacities, axes_opacities; CImgList bbox_colors, axes_colors; CImgList bbox_primitives, axes_primitives; float nambient = ambient_light, dx = 0, dy = 0, dz = 0, ratio = 1; T minval = (T)0, maxval = (T)255; if (disp.normalization) minval = colors.minmax(maxval); const float meanval = (float)mean(); bool color_model = true; if (cimg::abs(meanval-minval)>cimg::abs(meanval-maxval)) color_model = false; const CImg bgcolor(1,1,1,dim,color_model?minval:maxval), fgcolor(1,1,1,dim,color_model?maxval:minval); float xm, xM = (float)points.get_shared_line(0).maxmin(xm), ym, yM = (float)points.get_shared_line(1).maxmin(ym), zm, zM = (float)points.get_shared_line(2).maxmin(zm), delta = cimg::max(xM-xm,yM-ym,zM-zm); if (display_axes) { axes_points.assign(7,3); rotated_axes_points.assign(7,3); axes_opacities.assign(3,1,1,1,1,1.0f); axes_colors.assign(3,dim,1,1,1,fgcolor[0]); axes_points(0,0) = 0; axes_points(0,1) = 0; axes_points(0,2) = 0; axes_points(1,0) = 20; axes_points(1,1) = 0; axes_points(1,2) = 0; axes_points(2,0) = 0; axes_points(2,1) = 20; axes_points(2,2) = 0; axes_points(3,0) = 0; axes_points(3,1) = 0; axes_points(3,2) = 20; axes_points(4,0) = 22; axes_points(4,1) = -6; axes_points(4,2) = 0; axes_points(5,0) = -6; axes_points(5,1) = 22; axes_points(5,2) = 0; axes_points(6,0) = -6; axes_points(6,1) = -6; axes_points(6,2) = 22; axes_primitives.insert(CImg::vector(0,1)); axes_primitives.insert(CImg::vector(0,2)); axes_primitives.insert(CImg::vector(0,3)); } // Begin user interaction loop CImg visu0(*this), visu; bool init = true, clicked = false, redraw = true; unsigned int key = 0; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; const unsigned int old_events = disp.events; disp.show().button = disp.key = 0; disp.events = 3; while (!disp.is_closed && !key) { // Init object position and scale if necessary if (init) { ratio = delta>0?(2.0f*cimg::min(disp.width,disp.height)/(3.0f*delta)):0; dx = 0.5f*(xM+xm); dy = 0.5f*(yM+ym); dz = 0.5f*(zM+zm); if (centering) { cimg_forX(centered_points,l) { centered_points(l,0) = (float)((points(l,0)-dx)*ratio); centered_points(l,1) = (float)((points(l,1)-dy)*ratio); centered_points(l,2) = (float)((points(l,2)-dz)*ratio); } } if (render_static<0 || render_motion<0) { bbox_colors.assign(12,dim,1,1,1,fgcolor[0]); bbox_primitives.assign(12,1,2); bbox_points.assign(8,3); rotated_bbox_points.assign(8,3); bbox_points(0,0) = xm; bbox_points(0,1) = ym; bbox_points(0,2) = zm; bbox_points(1,0) = xM; bbox_points(1,1) = ym; bbox_points(1,2) = zm; bbox_points(2,0) = xM; bbox_points(2,1) = yM; bbox_points(2,2) = zm; bbox_points(3,0) = xm; bbox_points(3,1) = yM; bbox_points(3,2) = zm; bbox_points(4,0) = xm; bbox_points(4,1) = ym; bbox_points(4,2) = zM; bbox_points(5,0) = xM; bbox_points(5,1) = ym; bbox_points(5,2) = zM; bbox_points(6,0) = xM; bbox_points(6,1) = yM; bbox_points(6,2) = zM; bbox_points(7,0) = xm; bbox_points(7,1) = yM; bbox_points(7,2) = zM; bbox_primitives[0].fill(0,1); bbox_primitives[1].fill(1,2); bbox_primitives[2].fill(2,3); bbox_primitives[3].fill(3,0); bbox_primitives[4].fill(4,5); bbox_primitives[5].fill(5,6); bbox_primitives[6].fill(6,7); bbox_primitives[7].fill(7,4); bbox_primitives[8].fill(0,4); bbox_primitives[9].fill(1,5); bbox_primitives[10].fill(2,6); bbox_primitives[11].fill(3,7); bbox_opacities.assign(bbox_primitives.size,1,1,1,1,1.0f); } if (!pose) { if (pose_matrix) pose = CImg(pose_matrix,4,4,1,1,false); else pose = CImg::identity_matrix(4); } init = false; redraw = true; } // Rotate and Draw 3D object if (redraw) { const float r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0), r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1), r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2); if ((clicked && render_motion>=0) || (!clicked && render_static>=0)) { if (centering) cimg_forX(centered_points,l) { const float x = centered_points(l,0), y = centered_points(l,1), z = centered_points(l,2); rotated_points(l,0) = r00*x + r10*y + r20*z + r30; rotated_points(l,1) = r01*x + r11*y + r21*z + r31; rotated_points(l,2) = r02*x + r12*y + r22*z + r32; } else cimg_forX(points,l) { const float x = (float)points(l,0), y = (float)points(l,1), z = (float)points(l,2); rotated_points(l,0) = r00*x + r10*y + r20*z + r30; rotated_points(l,1) = r01*x + r11*y + r21*z + r31; rotated_points(l,2) = r02*x + r12*y + r22*z + r32; } } else { if (!centering) cimg_forX(bbox_points,l) { const float x = bbox_points(l,0), y = bbox_points(l,1), z = bbox_points(l,2); rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30; rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31; rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32; } else cimg_forX(bbox_points,l) { const float x = (bbox_points(l,0)-dx)*ratio, y = (bbox_points(l,1)-dy)*ratio, z = (bbox_points(l,2)-dz)*ratio; rotated_bbox_points(l,0) = r00*x + r10*y + r20*z + r30; rotated_bbox_points(l,1) = r01*x + r11*y + r21*z + r31; rotated_bbox_points(l,2) = r02*x + r12*y + r22*z + r32; } } // Draw object visu = visu0; if ((clicked && render_motion<0) || (!clicked && render_static<0)) visu.draw_object3d(visu.width/2.0f, visu.height/2.0f, 0, rotated_bbox_points,bbox_primitives,bbox_colors,bbox_opacities,1, false,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000.0f,0.2f); else visu.draw_object3d(visu.width/2.0f, visu.height/2.0f, 0, rotated_points,primitives,colors,opacities,clicked?render_motion:render_static, double_sided,focale,visu.dimx()/2.0f,visu.dimy()/2.0f,-5000.0f,nambient); // Draw axes if (display_axes) { const float Xaxes = 25.0f, Yaxes = visu.height-35.0f; cimg_forX(axes_points,l) { const float x = axes_points(l,0), y = axes_points(l,1), z = axes_points(l,2); rotated_axes_points(l,0) = r00*x + r10*y + r20*z; rotated_axes_points(l,1) = r01*x + r11*y + r21*z; rotated_axes_points(l,2) = r02*x + r12*y + r22*z; } axes_opacities(0,0) = (rotated_axes_points(1,2)>0)?0.5f:1.0f; axes_opacities(1,0) = (rotated_axes_points(2,2)>0)?0.5f:1.0f; axes_opacities(2,0) = (rotated_axes_points(3,2)>0)?0.5f:1.0f; visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_points,axes_primitives,axes_colors,axes_opacities,1,false,focale,0,0,0,0). draw_text("X",(int)(Xaxes+rotated_axes_points(4,0)), (int)(Yaxes+rotated_axes_points(4,1)), axes_colors[0].data, (T*)0, 11, axes_opacities(0,0)). draw_text("Y",(int)(Xaxes+rotated_axes_points(5,0)), (int)(Yaxes+rotated_axes_points(5,1)), axes_colors[1].data, (T*)0, 11, axes_opacities(1,0)). draw_text("Z",(int)(Xaxes+rotated_axes_points(6,0)), (int)(Yaxes+rotated_axes_points(6,1)), axes_colors[2].data, (T*)0, 11, axes_opacities(2,0)); } visu.display(disp); if (!clicked || render_motion==render_static) redraw = false; } // Handle user interaction disp.wait(); if ((disp.button || disp.wheel) && disp.mouse_x>=0 && disp.mouse_y>=0) { redraw = true; if (!clicked) { x0 = x1 = disp.mouse_x; y0 = y1 = disp.mouse_y; if (!disp.wheel) clicked = true; } else { x1 = disp.mouse_x; y1 = disp.mouse_y; } if (disp.button&1) { const float R = 0.45f*cimg::min(disp.width,disp.height), R2 = R*R, u0 = (float)(x0-disp.dimx()/2), v0 = (float)(y0-disp.dimy()/2), u1 = (float)(x1-disp.dimx()/2), v1 = (float)(y1-disp.dimy()/2), n0 = (float)std::sqrt(u0*u0+v0*v0), n1 = (float)std::sqrt(u1*u1+v1*v1), nu0 = n0>R?(u0*R/n0):u0, nv0 = n0>R?(v0*R/n0):v0, nw0 = (float)std::sqrt(cimg::max(0.0f,R2-nu0*nu0-nv0*nv0)), nu1 = n1>R?(u1*R/n1):u1, nv1 = n1>R?(v1*R/n1):v1, nw1 = (float)std::sqrt(cimg::max(0.0f,R2-nu1*nu1-nv1*nv1)), u = nv0*nw1-nw0*nv1, v = nw0*nu1-nu0*nw1, w = nv0*nu1-nu0*nv1, n = (float)std::sqrt(u*u+v*v+w*w), alpha = (float)std::asin(n/R2); rot_mat = CImg::rotation_matrix(u,v,w,alpha); rot_mat *= pose.get_crop(0,0,2,2); pose.draw_image(rot_mat,0,0); x0=x1; y0=y1; } if (disp.button&2) { pose(3,2)+=(y1-y0); x0 = x1; y0 = y1; } if (disp.wheel) { pose(3,2)-=15*disp.wheel; disp.wheel = 0; } if (disp.button&4) { pose(3,0)+=(x1-x0); pose(3,1)+=(y1-y0); x0 = x1; y0 = y1; } if ((disp.button&1) && (disp.button&2)) { init = true; disp.button = 0; x0 = x1; y0 = y1; pose = CImg::identity_matrix(4); } } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; } key = disp.key; if (key && key!=cimg::keyCTRLLEFT && key!=cimg::keyCTRLRIGHT) { if (disp.is_key(cimg::keyCTRLLEFT,true) || disp.is_key(cimg::keyCTRLRIGHT,true)) { switch (key) { case cimg::keyPAGEDOWN: nambient-=0.1f; if (nambient<-2) nambient = -2; redraw = true; break; case cimg::keyPAGEUP: nambient+=0.1f; if (nambient>2) nambient = 2; redraw = true; break; case cimg::keyD: if (disp.is_fullscreen) disp.toggle_fullscreen(); disp.resize(-200,-200); disp.is_resized = true; break; case cimg::keyC: if (disp.is_fullscreen) disp.toggle_fullscreen(); disp.resize(-50,-50); disp.is_resized = true; break; case cimg::keyR: if (disp.is_fullscreen) disp.toggle_fullscreen(); disp.resize(*this); disp.is_resized = true; break; case cimg::keyF: disp.resize(disp.screen_dimx(),disp.screen_dimy()).toggle_fullscreen().is_resized = true; break; case cimg::keyS: // Save snapshot { static unsigned int snap_number = 0; char filename[32] = { 0 }; std::FILE *file; do { std::sprintf(filename,"CImg_%.4u.bmp",snap_number++); if ((file=std::fopen(filename,"r"))!=0) std::fclose(file); } while (file); (+visu).draw_text(2,2,fgcolor,bgcolor,11,1.0f,"Saving snapshot...",filename).display(disp); visu.save(filename); visu.draw_text(2,2,fgcolor,bgcolor,11,1.0f,"Snapshot '%s' saved.",filename).display(disp); } break; case cimg::keyO: // Save object as an .OFF file { static unsigned int snap_number = 0; char filename[32] = { 0 }; std::FILE *file; do { std::sprintf(filename,"CImg_%.4u.off",snap_number++); if ((file=std::fopen(filename,"r"))!=0) std::fclose(file); } while (file); visu.draw_text(2,2,fgcolor,bgcolor,11,1.0f,"Saving object...",filename).display(disp); points.save_off(filename,primitives,colors); visu.draw_text(2,2,fgcolor,bgcolor,11,1.0f,"Object '%s' saved.",filename).display(disp); } break; } disp.key = key = 0; } } else key = 0; if (disp.is_resized) { disp.resize(false); visu0 = get_resize(disp,1); redraw = true; } } if (pose_matrix) std::memcpy(pose_matrix,pose.data,16*sizeof(float)); disp.events = old_events; disp.button = 0; return *this; } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImgList& opacities, CImgDisplay &disp, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) const { CImg npoints(points.size,3,1,1,0); tp *ptrX = npoints.data, *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); cimg_forX(npoints,l) { const CImg& point = points[l]; const unsigned int siz = point.size(); if (!siz) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (size=%u) contains a null element at " "position %u.",pixel_type(),points.size,l); *(ptrZ++) = (siz>2)?point(2):0; *(ptrY++) = (siz>1)?point(1):0; *(ptrX++) = point(0); } return display_object3d(npoints,primitives,colors,opacities,disp,centering, render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const CImg& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, CImgDisplay& disp, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) const { CImgList nopacities(opacities.size(),1); cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); return display_object3d(points,primitives,colors,nopacities,disp,centering, render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const CImgList& points, const CImgList& primitives, const CImgList& colors, const CImg& opacities, CImgDisplay& disp, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) const { CImgList nopacities(opacities.size(),1); cimglist_for(nopacities,l) nopacities(l,0) = opacities(l); if (!points) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points are empty.", pixel_type()); CImg npoints(points.size,3,1,1,0); tp *ptrX = npoints.data, *ptrY = npoints.ptr(0,1), *ptrZ = npoints.ptr(0,2); { cimg_forX(npoints,l) { const CImg& point = points[l]; const unsigned int siz = point.size(); if (!siz) throw CImgArgumentException("CImg<%s>::display_object3d() : Given points (size=%u) contains a null element at " "position %u.",pixel_type(),points.size,l); *(ptrZ++) = (siz>2)?point(2):0; *(ptrY++) = (siz>1)?point(1):0; *(ptrX++) = point(0); } } return display_object3d(npoints,primitives,colors,nopacities,disp,centering, render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const tp& points, const CImgList& primitives, const CImgList& colors, const to& opacities, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0) const { CImgDisplay disp(width,height,0,0); return display_object3d(points,primitives,colors,opacities,disp,centering, render_static,render_motion,double_sided,focale,ambient_light, display_axes,pose_matrix); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const tp& points, const CImgList& primitives, const CImgList& colors, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0, const float opacity=1.0f) const { CImgDisplay disp(width,height," ",0); return display_object3d(points,primitives,colors,CImg::vector(opacity), disp,centering,render_static,render_motion,double_sided, focale,ambient_light,display_axes,pose_matrix); } //! High-level interface for displaying a 3d object template const CImg& display_object3d(const tp& points, const CImgList& primitives, const CImgList& colors, CImgDisplay &disp, const bool centering=true, const int render_static=4, const int render_motion=1, const bool double_sided=false, const float focale=500.0f, const float ambient_light=0.05f, const bool display_axes=true, float *const pose_matrix=0, const float opacity=1.0f) const { return display_object3d(points,primitives,colors,CImg::vector(opacity), disp,centering,render_static,render_motion,double_sided, focale,ambient_light,display_axes,pose_matrix); } //@} //---------------------- // //! \name Input-Output //@{ //---------------------- //! Load an image from a file. /** \param filename = name of the image file to load. \note The extension of \c filename defines the file format. If no filename extension is provided, CImg::get_load() will try to load a CRAW file (CImg Raw file). **/ static CImg get_load(const char *const filename) { return CImg().load(filename); } CImg& load(const char *const filename) { const char *ext = cimg::filename_split(filename); #ifdef cimg_load_plugin cimg_load_plugin(filename); #endif if (!cimg::strncasecmp(ext,"asc",3)) return load_ascii(filename); if (!cimg::strncasecmp(ext,"dlm",3) || !cimg::strncasecmp(ext,"txt",3)) return load_dlm(filename); if (!cimg::strncasecmp(ext,"inr",3)) return load_inr(filename); if (!cimg::strncasecmp(ext,"hdr",3) || !cimg::strncasecmp(ext,"nii",3)) return load_analyze(filename); if (!cimg::strncasecmp(ext,"par",3) || !cimg::strncasecmp(ext,"rec",3)) return load_parrec(filename); if (!cimg::strncasecmp(ext,"pan",3)) return load_pandore(filename); if (!cimg::strncasecmp(ext,"bmp",3)) return load_bmp(filename); if (!cimg::strncasecmp(ext,"png",3)) return load_png(filename); if (!cimg::strncasecmp(ext,"tif",3)) return load_tiff(filename); if (!cimg::strncasecmp(ext,"jpg",3) || !cimg::strncasecmp(ext,"jpeg",4)) return load_jpeg(filename); if (!cimg::strncasecmp(ext,"ppm",3) || !cimg::strncasecmp(ext,"pgm",3) || !cimg::strncasecmp(ext,"pnm",3)) return load_pnm(filename); if (!cimg::strncasecmp(ext,"cimg",4) || ext[0]=='\0') return load_cimg(filename); if (!cimg::strncasecmp(ext,"dcm",3) || !cimg::strncasecmp(ext,"dicom",5)) return load_dicom(filename); return load_other(filename); } //! Load an image from an ASCII file. static CImg get_load_ascii(std::FILE *const file, const char *const filename=0) { return CImg().load_ascii(file,filename); } CImg& load_ascii(std::FILE *const file, const char *const filename=0) { std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); char line[256] = { 0 }; std::fscanf(nfile,"%255[^\n]",line); unsigned int off, dx = 0, dy = 1, dz = 1, dv = 1; int err = 1; std::sscanf(line,"%u %u %u %u",&dx,&dy,&dz,&dv); if (!dx || !dy || !dz || !dv) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_ascii() : File '%s' is not a valid .ASC file.\n" "Specified image dimensions are (%u,%u,%u,%u).", pixel_type(),filename?filename:"(FILE*)",dx,dy,dz,dv); } assign(dx,dy,dz,dv); const unsigned int siz = size(); double val; T *ptr = data; for (off=0; off::load_ascii() : File '%s', only %u/%u values read.", pixel_type(),filename?filename:"(FILE*)",off,siz); if (!file) cimg::fclose(nfile); return *this; } //! Load an image from an ASCII file. static CImg get_load_ascii(const char *const filename) { return CImg().load_ascii(0,filename); } CImg& load_ascii(const char *const filename) { return load_ascii(0,filename); } //! Load an image from a DLM file. static CImg get_load_dlm(std::FILE *const file, const char *const filename=0) { return CImg().load_dlm(file,filename); } CImg& load_dlm(std::FILE *const file, const char *const filename=0) { std::FILE *const nfile = file?file:cimg::fopen(filename,"r"); assign(256,256); char c, delimiter[256] = { 0 }, tmp[256]; unsigned int cdx = 0, dx = 0, dy = 0; int oerr = 0, err; double val; while ((err = std::fscanf(nfile,"%lf%255[^0-9.eE+-]",&val,delimiter))!=EOF) { oerr = err; if (err>0) (*this)(cdx++,dy) = (T)val; if (cdx>=width) resize(width+256,1,1,1,0); c = 0; if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') { dx = cimg::max(cdx,dx); ++dy; if (dy>=height) resize(width,height+256,1,1,0); cdx = 0; } } if (cdx && oerr==1) { dx=cdx; ++dy; } if (!dx || !dy) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_dlm() : File '%s' is not a valid DLM file.\n" "Specified image dimensions are (%u,%u).", pixel_type(),filename?filename:"(FILE*)",dx,dy); } resize(dx,dy,1,1,0); if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a DLM file. static CImg get_load_dlm(const char *const filename) { return CImg().load_dlm(0,filename); } CImg& load_dlm(const char *const filename) { return load_dlm(0,filename); } //! Load an image from a PNM file. static CImg get_load_pnm(std::FILE *const file, const char *const filename=0) { return CImg().load_pnm(file, filename); } CImg& load_pnm(std::FILE *const file, const char *const filename=0) { std::FILE *const nfile=file?file:cimg::fopen(filename,"rb"); unsigned int ppm_type, W, H, colormax=255; char item[1024] = { 0 }; int err, rval, gval, bval; const int cimg_iobuffer = 12*1024*1024; while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); if(std::sscanf(item," P%u",&ppm_type)!=1) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PNM header 'P?' not found.", pixel_type(),filename?filename:"(FILE*)"); } while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); if ((err=std::sscanf(item," %u %u %u",&W,&H,&colormax))<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', WIDTH and HEIGHT fields are not defined in PNM header.", pixel_type(),filename?filename:"(FILE*)"); } if (err==2) { while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (item[0]=='#' || !err)) std::fgetc(nfile); if (std::sscanf(item,"%u",&colormax)!=1) cimg::warn("CImg<%s>::load_pnm() : File '%s', COLORMAX field is not defined in PNM header.", pixel_type(),filename?filename:"(FILE*)"); } std::fgetc(nfile); assign(); switch (ppm_type) { case 2: // Grey Ascii { assign(W,H,1,1); T* rdata = data; cimg_foroff(*this,off) { std::fscanf(nfile,"%d",&rval); *(rdata++) = (T)rval; } } break; case 3: // Color Ascii { assign(W,H,1,3); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); cimg_forXY(*this,x,y) { std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval); *(rdata++) = (T)rval; *(gdata++) = (T)gval; *(bdata++) = (T)bval; } } break; case 5: // Grey Binary { if (colormax<256) // 8 bits { CImg raw; assign(W,H,1,1); T *ptrd = ptr(0,0,0,0); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); } } else // 16 bits { CImg raw; assign(W,H,1,1); T *ptrd = ptr(0,0,0,0); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer/2)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned short *ptrs = raw.data; for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); } } } break; case 6: // Color Binary { if (colormax<256) // 8 bits { CImg raw; assign(W,H,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } } else // 16 bits { CImg raw; assign(W,H,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer/2)); cimg::fread(raw.data,raw.width,nfile); if (!cimg::endian()) cimg::endian_swap(raw.data,raw.width); toread-=raw.width; const unsigned short *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } } } break; default: if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pnm() : File '%s', PPM type 'P%d' not supported.", pixel_type(),filename?filename:"(FILE*)",ppm_type); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a PNM file. static CImg get_load_pnm(const char *const filename) { return get_load_pnm(0,filename); } CImg& load_pnm(const char *const filename) { return load_pnm(0,filename); } //! Load an image from a BMP file. static CImg get_load_bmp(std::FILE *const file, const char *const filename=0) { return CImg().load_bmp(file,filename); } CImg& load_bmp(std::FILE *const file, const char *const filename=0) { std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); unsigned char header[64]; cimg::fread(header,54,nfile); if (header[0]!='B' || header[1]!='M') { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_bmp() : File '%s' is not a valid BMP file.", pixel_type(),filename?filename:"(FILE*)"); } assign(); // Read header and pixel buffer int file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24), offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24), dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24), dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24), compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24), nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24), bpp = header[0x1C] + (header[0x1D]<<8), *palette = 0; const int dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)), align = (4-dx_bytes%4)%4, buf_size = cimg::min(cimg::abs(dy)*(dx_bytes+align),file_size-offset); if (bpp<16) { if (!nb_colors) nb_colors=1<0) std::fseek(nfile,xoffset,SEEK_CUR); unsigned char *buffer = new unsigned char[buf_size], *ptrs = buffer; cimg::fread(buffer,buf_size,nfile); if (!file) cimg::fclose(nfile); // Decompress buffer (if necessary) if (compression) { delete[] buffer; if (file) { throw CImgIOException("CImg<%s>::load_bmp() : Not able to read a compressed BMP file using a *FILE input",pixel_type()); } else return load_other(filename); } // Read pixel data assign(dx,cimg::abs(dy),1,3); switch (bpp) { case 1: // Monochrome { for (int y=height-1; y>=0; --y) { unsigned char mask = 0x80, val = 0; cimg_forX(*this,x) { if (mask==0x80) val = *(ptrs++); const unsigned char *col = (unsigned char*)(palette+(val&mask?1:0)); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); mask = cimg::ror(mask); } ptrs+=align; } } break; case 4: // 16 colors { for (int y=height-1; y>=0; --y) { unsigned char mask = 0xF0, val = 0; cimg_forX(*this,x) { if (mask==0xF0) val = *(ptrs++); const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4)); unsigned char *col = (unsigned char*)(palette+color); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); mask = cimg::ror(mask,4); } ptrs+=align; } } break; case 8: // 256 colors { for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { const unsigned char *col = (unsigned char*)(palette+*(ptrs++)); (*this)(x,y,2) = (T)*(col++); (*this)(x,y,1) = (T)*(col++); (*this)(x,y,0) = (T)*(col++); } ptrs+=align; } } break; case 16: // 16 bits colors { for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { const unsigned char c1 = *(ptrs++), c2 = *(ptrs++); const unsigned short col = (unsigned short)(c1|(c2<<8)); (*this)(x,y,2) = (T)(col&0x1F); (*this)(x,y,1) = (T)((col>>5)&0x1F); (*this)(x,y,0) = (T)((col>>10)&0x1F); } ptrs+=align; } } break; case 24: // 24 bits colors { for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); (*this)(x,y,0) = (T)*(ptrs++); } ptrs+=align; } } break; case 32: // 32 bits colors { for (int y=height-1; y>=0; --y) { cimg_forX(*this,x) { (*this)(x,y,2) = (T)*(ptrs++); (*this)(x,y,1) = (T)*(ptrs++); (*this)(x,y,0) = (T)*(ptrs++); ++ptrs; } ptrs+=align; } } break; } if (palette) delete[] palette; delete[] buffer; if (dy<0) mirror('y'); return *this; } //! Load an image from a BMP file. static CImg get_load_bmp(const char *const filename) { return CImg().load_bmp(0,filename); } CImg& load_bmp(const char *const filename) { return load_bmp(0,filename); } //! Load an image from a PNG file. // (Note : Most of this function has been written by Eric Fausett) static CImg get_load_png(std::FILE *const file, const char *const filename=0) { return CImg().load_png(file,filename); } CImg& load_png(std::FILE *const file, const char *const filename=0) { #ifndef cimg_use_png if (file) throw CImgIOException("CImg<%s>::load_png() : File '(FILE*)' cannot be read without using libpng.",pixel_type()); else return load_other(filename); #else // Open file and check for PNG validity std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); unsigned char pngCheck[8]; cimg::fread(pngCheck,8,nfile); if (png_sig_cmp(pngCheck,0,8)) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_png() : File '%s' is not a valid PNG file.", pixel_type(),filename?filename:"(FILE*)"); } // Setup PNG structures for read png_voidp user_error_ptr = 0; png_error_ptr user_error_fn = 0, user_warning_fn = 0; png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, // Verifies libpng version correct user_error_ptr, user_error_fn, user_warning_fn); if(!png_ptr) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'png_ptr' data structure.", pixel_type(),filename?filename:"(FILE*)"); } png_infop info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, (png_infopp)0, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'info_ptr' data structure.", pixel_type(),filename?filename:"(FILE*)"); } png_infop end_info = png_create_info_struct(png_ptr); if(!end_info) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', trouble initializing 'end_info' data structure.", pixel_type(),filename?filename:"(FILE*)"); } // Error handling callback for png file reading if (setjmp(png_jmpbuf(png_ptr))) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', unknown fatal error.", pixel_type(),filename?filename:"(FILE*)"); } png_init_io(png_ptr, nfile); png_set_sig_bytes(png_ptr, 8); // Get PNG Header Info up to data block png_read_info(png_ptr, info_ptr); png_uint_32 W, H; int bit_depth, color_type, interlace_type; png_get_IHDR(png_ptr, info_ptr, &W, &H, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL); int new_bit_depth = bit_depth; int new_color_type = color_type; // Transforms to unify image data if (new_color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); new_color_type -= PNG_COLOR_MASK_PALETTE; new_bit_depth = 8; } if (new_color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_gray_1_2_4_to_8(png_ptr); new_bit_depth = 8; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (new_color_type == PNG_COLOR_TYPE_GRAY || new_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); new_color_type |= PNG_COLOR_MASK_COLOR; } if (new_color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0xffffU, PNG_FILLER_AFTER); png_read_update_info(png_ptr, info_ptr); if (!(new_bit_depth==8 || new_bit_depth==16)) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', wrong bit coding (bit_depth=%u)", pixel_type(),filename?filename:"(FILE*)",new_bit_depth); } const int byte_depth = new_bit_depth>>3; // Allocate Memory for Image Read png_bytep *imgData = new png_bytep[H]; for (unsigned int row=0; row < H; ++row) imgData[row] = new png_byte[byte_depth * 4 * W]; png_read_image(png_ptr, imgData); png_read_end(png_ptr, end_info); // Read pixel data if (!(new_color_type==PNG_COLOR_TYPE_RGB || new_color_type==PNG_COLOR_TYPE_RGB_ALPHA)) { if (!file) cimg::fclose(nfile); png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0); throw CImgIOException("CImg<%s>::load_png() : File '%s', wrong color coding (new_color_type=%u)", pixel_type(),filename?filename:"(FILE*)",new_color_type); } const bool no_alpha_channel = (new_color_type==PNG_COLOR_TYPE_RGB); assign(W,H,1,no_alpha_channel?3:4); T *ptr1 = ptr(0,0,0,0), *ptr2 = ptr(0,0,0,1), *ptr3 = ptr(0,0,0,2), *ptr4 = ptr(0,0,0,3); switch(new_bit_depth) { case 8: { cimg_forY(*this,y) { const unsigned char *ptrs = (unsigned char*)imgData[y]; cimg_forX(*this,x) { *(ptr1++) = (T)*(ptrs++); *(ptr2++) = (T)*(ptrs++); *(ptr3++) = (T)*(ptrs++); if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); } } } break; case 16: { cimg_forY(*this,y) { const unsigned short *ptrs = (unsigned short*)(imgData[y]); cimg_forX(*this,x) { *(ptr1++) = (T)*(ptrs++); *(ptr2++) = (T)*(ptrs++); *(ptr3++) = (T)*(ptrs++); if (no_alpha_channel) ++ptrs; else *(ptr4++) = (T)*(ptrs++); } } } break; } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); // Deallocate Image Read Memory for (unsigned int n=0; n().load_png(0,filename); } CImg& load_png(const char *const filename) { return load_png(0,filename); } //! Load an image from a TIFF file. // (Original contribution by Jerome Boulanger). static CImg get_load_tiff(const char *const filename) { return CImg().load_tiff(filename); } CImg& load_tiff(const char *const filename) { #ifndef cimg_use_tiff return load_other(filename); #else TIFF *tif = TIFFOpen(filename,"r"); #if cimg_debug>=3 TIFFSetWarningHandler(0); TIFFSetErrorHandler(0); #endif if (tif) { unsigned int number_of_directories = 0; do ++number_of_directories; while (TIFFReadDirectory(tif)); uint16 samplesperpixel, bitspersample; uint32 nx,ny; TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx); TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny); TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel); if (samplesperpixel!=1 && samplesperpixel!=3 && samplesperpixel!=4) { cimg::warn("CImg<%s>::load_tiff() : File '%s', unknow value for tag : TIFFTAG_SAMPLESPERPIXEL, will force it to 1.", pixel_type(),filename?filename:"(FILE*)"); samplesperpixel=1; } TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample); TIFFClose(tif); tif = TIFFOpen(filename,"r"); assign(nx,ny,number_of_directories,samplesperpixel); unsigned int dir = 0; do { if (bitspersample!=8 || !(samplesperpixel == 3 || samplesperpixel == 4)) { uint16 photo, config; TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config); TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo); if (TIFFIsTiled(tif)) { uint32 tw, th; TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tw); TIFFGetField(tif, TIFFTAG_TILELENGTH, &th); if (config==PLANARCONFIG_CONTIG) switch(bitspersample) { case 8: { unsigned char *buf = (unsigned char*)_TIFFmalloc(TIFFTileSize(tif)); if (buf) { for (unsigned int row = 0; row::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { unsigned char *ptr = buf; for (unsigned int rr=row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { unsigned short *ptr = buf; for (unsigned int rr=row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { float *ptr = buf; for (unsigned int rr=row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { unsigned char *ptr = buf; for (unsigned int rr=row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { unsigned short *ptr = buf; for (unsigned int rr=row; rr::load_tiff() : File '%s', an error occure while reading a tile.", pixel_type(),filename?filename:"(FILE*)"); } else { float *ptr = buf; for (unsigned int rr=row; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } unsigned char *ptr = buf; for (unsigned int rr=0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } unsigned short *ptr = buf; for (unsigned int rr=0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, 0); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } float *ptr = buf; for (unsigned int rr=0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', an error occure while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } unsigned char *ptr = buf; for (unsigned int rr=0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } unsigned short *ptr = buf; for (unsigned int rr=0; rrny?ny-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(tif, row, vv); if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) { _TIFFfree(buf); TIFFClose(tif); throw CImgException("CImg<%s>::load_tiff() : File '%s', error while reading a strip.", pixel_type(),filename?filename:"(FILE*)"); } float *ptr = buf; for (unsigned int rr=0; rr::load_tiff() : File '%s', not enough memory for buffer allocation.", pixel_type(),filename?filename:"(FILE*)"); } TIFFReadRGBAImage(tif,nx,ny,raster,0); switch (samplesperpixel) { case 1: { cimg_forXY(*this,x,y) (*this)(x,y,dir) = (T)(float)((raster[nx*(ny-1-y)+x]+ 128) / 257); } break; case 3: { cimg_forXY(*this,x,y) { (*this)(x,y,dir,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); (*this)(x,y,dir,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); (*this)(x,y,dir,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); } } break; case 4: { cimg_forXY(*this,x,y) { (*this)(x,y,dir,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]); (*this)(x,y,dir,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]); (*this)(x,y,dir,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]); (*this)(x,y,dir,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]); } } break; } _TIFFfree(raster); } ++dir; } while (TIFFReadDirectory(tif)); TIFFClose(tif); } else throw CImgException("CImg<%s>::load_tiff() : File '%s', error while loading the image.", pixel_type(),filename?filename:"(FILE*)"); return *this; #endif } //! Load an image from a JPEG file. static CImg get_load_jpeg(std::FILE *const file, const char *const filename=0) { return CImg().load_jpeg(file,filename); } CImg& load_jpeg(std::FILE *const file, const char *const filename=0) { #ifndef cimg_use_jpeg if (file) throw CImgIOException("CImg<%s>::load_jpeg() : File '(FILE*)' cannot be read without using libjpeg.", pixel_type()); else return load_other(filename); #else struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo,nfile); jpeg_read_header(&cinfo,TRUE); jpeg_start_decompress(&cinfo); if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) { cimg::warn("CImg<%s>::load_jpeg() : Don't know how to read image '%s' with libpeg, trying ImageMagick's convert", pixel_type(),filename?filename:"(unknown)"); if (!file) return load_other(filename); else { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_jpeg() : Cannot read JPEG image '%s' using a *FILE input.", pixel_type(),filename?filename:"(FILE*)"); } } const unsigned int row_stride = cinfo.output_width * cinfo.output_components; unsigned char *buf = new unsigned char[cinfo.output_width*cinfo.output_height*cinfo.output_components], *buf2 = buf; JSAMPROW row_pointer[1]; while (cinfo.output_scanline < cinfo.output_height) { row_pointer[0] = &buf[cinfo.output_scanline*row_stride]; jpeg_read_scanlines(&cinfo,row_pointer,1); } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); if (!file) cimg::fclose(nfile); assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); switch (dim) { case 1: { T *ptr_g = data; cimg_forXY(*this,x,y) *(ptr_g++) = (T)*(buf2++); } break; case 3: { T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); cimg_forXY(*this,x,y) { *(ptr_r++) = (T)*(buf2++); *(ptr_g++) = (T)*(buf2++); *(ptr_b++) = (T)*(buf2++); } } break; case 4: { T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); cimg_forXY(*this,x,y) { *(ptr_r++) = (T)*(buf2++); *(ptr_g++) = (T)*(buf2++); *(ptr_b++) = (T)*(buf2++); *(ptr_a++) = (T)*(buf2++); } } break; } delete[] buf; return *this; #endif } //! Load an image from a JPEG file. static CImg get_load_jpeg(const char *const filename) { return CImg().load_jpeg(0,filename); } CImg& load_jpeg(const char *const filename) { return load_jpeg(0,filename); } //! Load an image using builtin ImageMagick++ Library. /** Added April/may 2006 by Christoph Hormann This is experimental code, not much tested, use with care. **/ static CImg get_load_magick(const char *const filename) { return CImg().load_magick(filename); } CImg& load_magick(const char *const filename) { #ifdef cimg_use_magick Magick::Image image(filename); const unsigned int W = image.size().width(), H = image.size().height(); switch (image.type()) { case Magick::PaletteMatteType: case Magick::TrueColorMatteType: case Magick::ColorSeparationType: { assign(W,H,1,4); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2), *adata = ptr(0,0,0,3); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(rdata++) = (T)(pixels->red); *(gdata++) = (T)(pixels->green); *(bdata++) = (T)(pixels->blue); *(adata++) = (T)(pixels->opacity); ++pixels; } } break; case Magick::PaletteType: case Magick::TrueColorType: { assign(W,H,1,3); T *rdata = ptr(0,0,0,0), *gdata = ptr(0,0,0,1), *bdata = ptr(0,0,0,2); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(rdata++) = (T)(pixels->red); *(gdata++) = (T)(pixels->green); *(bdata++) = (T)(pixels->blue); ++pixels; } } break; case Magick::GrayscaleMatteType: { assign(W,H,1,2); T *data = ptr(0,0,0,0), *adata = ptr(0,0,0,1); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(data++) = (T)(pixels->red); *(adata++) = (T)(pixels->opacity); ++pixels; } } break; default: { assign(W,H,1,1); T *data = ptr(0,0,0,0); Magick::PixelPacket *pixels = image.getPixels(0,0,W,H); for (unsigned int off = W*H; off; --off) { *(data++) = (T)(pixels->red); ++pixels; } } break; } #else throw CImgIOException("CImg<%s>::load_magick() : File '%s', Magick++ has not been linked during compilation.", pixel_type(),filename?filename:"(null)"); #endif return *this; } //! Load an image from a .RAW file. static CImg get_load_raw(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed=false, const bool endian_swap=false) { return CImg().load_raw(file,filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap); } CImg& load_raw(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed = false, const bool endian_swap = false) { assign(sizex,sizey,sizez,sizev,0); const unsigned int siz = size(); if (siz) { std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); if (!multiplexed) { cimg::fread(data,siz,nfile); if (endian_swap) cimg::endian_swap(data,siz); } else { CImg buf(1,1,1,sizev); cimg_forXYZ(*this,x,y,z) { cimg::fread(buf.data,sizev,nfile); if (endian_swap) cimg::endian_swap(buf.data,sizev); set_vector_at(buf,x,y,z); } } if (!file) cimg::fclose(nfile); } return *this; } //! Load an image from a .RAW file. static CImg get_load_raw(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed = false, const bool endian_swap = false) { return CImg().load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap); } CImg& load_raw(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int sizez=1, const unsigned int sizev=1, const bool multiplexed = false, const bool endian_swap = false) { return load_raw(0,filename,sizex,sizey,sizez,sizev,multiplexed,endian_swap); } //! Load an image from a RGBA file. static CImg get_load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { return CImg().load_rgba(file,filename,dimw,dimh); } CImg& load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { const int cimg_iobuffer = 12*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,4); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/4; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); *(ptr_a++) = (T)*(ptrs++); } } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a RGBA file. static CImg get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh) { return CImg().load_rgba(0,filename,dimw,dimh); } CImg& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh) { return load_rgba(0,filename,dimw,dimh); } //! Load an image from a RGB file. static CImg get_load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { return CImg().load_rgb(file,filename,dimw,dimh); } CImg& load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) { const int cimg_iobuffer = 12*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); CImg raw; assign(dimw,dimh,1,3); T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2); for (int toread = (int)size(); toread>0; ) { raw.assign(cimg::min(toread,cimg_iobuffer)); cimg::fread(raw.data,raw.width,nfile); toread-=raw.width; const unsigned char *ptrs = raw.data; for (unsigned int off = raw.width/3; off; --off) { *(ptr_r++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_b++) = (T)*(ptrs++); } } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a RGB file. static CImg get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh) { return CImg().load_rgb(0,filename,dimw,dimh); } CImg& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh) { return load_rgb(0,filename,dimw,dimh); } //! Load an image from an INRIMAGE-4 file. static CImg get_load_inr(std::FILE *const file, const char *const filename=0, float *voxsize=0) { return CImg().load_inr(file,filename,voxsize); } CImg& load_inr(std::FILE *const file, const char *const filename=0, float *const voxsize=0) { #define cimg_load_inr_case(Tf,sign,pixsize,Ts) \ if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \ Ts *xval, *val = new Ts[fopt[0]*fopt[3]]; \ cimg_forYZ(*this,y,z) { \ cimg::fread(val,fopt[0]*fopt[3],nfile); \ if (fopt[7]!=endian) cimg::endian_swap(val,fopt[0]*fopt[3]); \ xval = val; cimg_forX(*this,x) cimg_forV(*this,k) (*this)(x,y,z,k) = (T)*(xval++); \ } \ delete[] val; \ loaded = true; \ } std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); int fopt[8], endian=cimg::endian()?1:0; bool loaded = false; if (voxsize) voxsize[0]=voxsize[1]=voxsize[2]=1; _load_inr(nfile,fopt,voxsize); assign(fopt[0],fopt[1],fopt[2],fopt[3]); cimg_load_inr_case(0,0,8, unsigned char); cimg_load_inr_case(0,1,8, char); cimg_load_inr_case(0,0,16,unsigned short); cimg_load_inr_case(0,1,16,short); cimg_load_inr_case(0,0,32,unsigned int); cimg_load_inr_case(0,1,32,int); cimg_load_inr_case(1,0,32,float); cimg_load_inr_case(1,1,32,float); cimg_load_inr_case(1,0,64,double); cimg_load_inr_case(1,1,64,double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_inr() : File '%s', cannot read images of the type specified in the file", pixel_type(),filename?filename:"(FILE*)"); } if (!file) cimg::fclose(nfile); return *this; } static void _load_inr(std::FILE *file, int out[8], float *const voxsize=0) { char item[1024], tmp1[64], tmp2[64]; out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1; std::fscanf(file,"%63s",item); if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0) throw CImgIOException("CImg<%s>::load_inr() : File does not appear to be a valid INR file.\n" "(INRIMAGE-4 identifier not found)",pixel_type()); while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && cimg::strncmp(item,"##}",3)) { std::sscanf(item," XDIM%*[^0-9]%d",out); std::sscanf(item," YDIM%*[^0-9]%d",out+1); std::sscanf(item," ZDIM%*[^0-9]%d",out+2); std::sscanf(item," VDIM%*[^0-9]%d",out+3); std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6); if (voxsize) { std::sscanf(item," VX%*[^0-9.eE+-]%f",voxsize); std::sscanf(item," VY%*[^0-9.eE+-]%f",voxsize+1); std::sscanf(item," VZ%*[^0-9.eE+-]%f",voxsize+2); } if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1; switch(std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) { case 0: break; case 2: out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strcpy(tmp1,tmp2); case 1: if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0; if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1; if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2; if (out[4]>=0) break; default: throw CImgIOException("cimg::inr_header_read() : Invalid TYPE '%s'",tmp2); } } if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0) throw CImgIOException("CImg<%s>::load_inr() : Bad dimensions in .inr file = ( %d , %d , %d , %d )", pixel_type(),out[0],out[1],out[2],out[3]); if(out[4]<0 || out[5]<0) throw CImgIOException("CImg<%s>::load_inr() : TYPE is not fully defined",pixel_type()); if(out[6]<0) throw CImgIOException("CImg<%s>::load_inr() : PIXSIZE is not fully defined",pixel_type()); if(out[7]<0) throw CImgIOException("CImg<%s>::load_inr() : Big/Little Endian coding type is not defined",pixel_type()); } //! Load an image from an INRIMAGE-4 file. static CImg get_load_inr(const char *const filename, float *const voxsize=0) { return CImg().load_inr(0,filename,voxsize); } CImg& load_inr(const char *const filename, float *const voxsize=0) { return load_inr(0,filename,voxsize); } //! Load an image from a PANDORE file. static CImg get_load_pandore(std::FILE *const file, const char *const filename=0) { return CImg().load_pandore(file,filename); } CImg& load_pandore(std::FILE *const file, const char *const filename) { #define cimg_load_pandore_case(nid,nbdim,nwidth,nheight,ndepth,ndim,stype) \ case nid: { \ cimg::fread(dims,nbdim,nfile); \ if (endian) cimg::endian_swap(dims,nbdim); \ assign(nwidth,nheight,ndepth,ndim); \ const unsigned int siz = size(); \ stype *buffer = new stype[siz]; \ cimg::fread(buffer,siz,nfile); \ if (endian) cimg::endian_swap(buffer,siz); \ T *ptrd = data; \ cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \ buffer-=siz; \ delete[] buffer; \ } \ break; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; char tmp[32]; cimg::fread(tmp,12,nfile); if (cimg::strncasecmp("PANDORE",tmp,7)) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pandore() : File '%s' is not a valid PANDORE file.\n" "(PANDORE identifier not found).",pixel_type(),filename?filename:"(FILE*)"); } unsigned int imageid,dims[8]; int ptbuf[4]; cimg::fread(&imageid,1,nfile); const bool endian = (imageid>255); if (endian) cimg::endian_swap(imageid); cimg::fread(tmp,20,nfile); switch (imageid) { cimg_load_pandore_case(2,2,dims[1],1,1,1,uchar); cimg_load_pandore_case(3,2,dims[1],1,1,1,long); cimg_load_pandore_case(4,2,dims[1],1,1,1,float); cimg_load_pandore_case(5,3,dims[2],dims[1],1,1,uchar); cimg_load_pandore_case(6,3,dims[2],dims[1],1,1,long); cimg_load_pandore_case(7,3,dims[2],dims[1],1,1,float); cimg_load_pandore_case(8,4,dims[3],dims[2],dims[1],1,uchar); cimg_load_pandore_case(9,4,dims[3],dims[2],dims[1],1,long); cimg_load_pandore_case(10,4,dims[3],dims[2],dims[1],1,float); case 11: // Region 1D { cimg::fread(dims,3,nfile); if (endian) cimg::endian_swap(dims,3); assign(dims[1],1,1,1); const unsigned siz = size(); if (dims[2]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[2]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned int *buffer = new unsigned int[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; case 12: // Region 2D { cimg::fread(dims,4,nfile); if (endian) cimg::endian_swap(dims,4); assign(dims[2],dims[1],1,1); const unsigned int siz = size(); if (dims[3]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[3]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned long *buffer = new unsigned long[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; case 13: // Region 3D { cimg::fread(dims,5,nfile); if (endian) cimg::endian_swap(dims,5); assign(dims[3],dims[2],dims[1],1); const unsigned int siz = size(); if (dims[4]<256) { unsigned char *buffer = new unsigned char[siz]; cimg::fread(buffer,siz,nfile); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { if (dims[4]<65536) { unsigned short *buffer = new unsigned short[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } else { unsigned int *buffer = new unsigned int[siz]; cimg::fread(buffer,siz,nfile); if (endian) cimg::endian_swap(buffer,siz); T *ptrd = data; cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); buffer-=siz; delete[] buffer; } } } break; cimg_load_pandore_case(16,4,dims[2],dims[1],1,3,uchar); cimg_load_pandore_case(17,4,dims[2],dims[1],1,3,long); cimg_load_pandore_case(18,4,dims[2],dims[1],1,3,float); cimg_load_pandore_case(19,5,dims[3],dims[2],dims[1],3,uchar); cimg_load_pandore_case(20,5,dims[3],dims[2],dims[1],3,long); cimg_load_pandore_case(21,5,dims[3],dims[2],dims[1],3,float); cimg_load_pandore_case(22,2,dims[1],1,1,dims[0],uchar); cimg_load_pandore_case(23,2,dims[1],1,1,dims[0],long); cimg_load_pandore_case(24,2,dims[1],1,1,dims[0],ulong); cimg_load_pandore_case(25,2,dims[1],1,1,dims[0],float); cimg_load_pandore_case(26,3,dims[2],dims[1],1,dims[0],uchar); cimg_load_pandore_case(27,3,dims[2],dims[1],1,dims[0],long); cimg_load_pandore_case(28,3,dims[2],dims[1],1,dims[0],ulong); cimg_load_pandore_case(29,3,dims[2],dims[1],1,dims[0],float); cimg_load_pandore_case(30,4,dims[3],dims[2],dims[1],dims[0],uchar); cimg_load_pandore_case(31,4,dims[3],dims[2],dims[1],dims[0],long); cimg_load_pandore_case(32,4,dims[3],dims[2],dims[1],dims[0],ulong); cimg_load_pandore_case(33,4,dims[3],dims[2],dims[1],dims[0],float); case 34: // Points 1D cimg::fread(ptbuf,1,nfile); if (endian) cimg::endian_swap(ptbuf,1); assign(1); (*this)(0) = (T)ptbuf[0]; break; case 35: // Points 2D cimg::fread(ptbuf,2,nfile); if (endian) cimg::endian_swap(ptbuf,2); assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0]; break; case 36: // Points 3D cimg::fread(ptbuf,3,nfile); if (endian) cimg::endian_swap(ptbuf,3); assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0]; break; default: if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_pandore() : File '%s', cannot read images with ID_type=%u", pixel_type(),filename?filename:"(FILE*)",imageid); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image from a PANDORE file. static CImg get_load_pandore(const char *const filename) { return CImg().load_pandore(0,filename); } CImg& load_pandore(const char *const filename) { return load_pandore(0,filename); } //! Load an image from an ANALYZE7.5/NIFTI file. static CImg get_load_analyze(const char *const filename, float *const voxsize=0) { return CImg().load_analyze(filename,voxsize); } CImg& load_analyze(const char *const filename, float *const voxsize=0) { std::FILE *file_header = 0, *file = 0; bool error_file = false; char body[1024]; const char *ext = cimg::filename_split(filename,body); if (!cimg::strncasecmp(ext,"nii",3)) file_header = cimg::fopen(filename,"rb"); else { if (!cimg::strncasecmp(ext,"hdr",3) || !cimg::strncasecmp(ext,"img",3)) { std::sprintf(body+cimg::strlen(body),".hdr"); file_header = cimg::fopen(body,"rb"); if (!file_header) error_file = true; else { std::sprintf(body+cimg::strlen(body)-3,"img"); file = cimg::fopen(body,"rb"); if (!file) { cimg::fclose(file_header); error_file = true; } } } } if (error_file) throw CImgIOException("CImg<%s>::load_analyze() : Filename '%s', not recognized as an Analyze 7.5 or NIFTI file.", pixel_type(),filename); // Read header bool endian = false; unsigned int header_size; cimg::fread(&header_size,1,file_header); if (header_size>=4096) { endian = true; cimg::endian_swap(header_size); } unsigned char *header = new unsigned char[header_size]; cimg::fread(header+4,header_size-4,file_header); if (file) cimg::fclose(file_header); if (endian) { cimg::endian_swap((short*)(header+40),5); cimg::endian_swap((short*)(header+70),1); cimg::endian_swap((short*)(header+72),1); cimg::endian_swap((float*)(header+76),4); cimg::endian_swap((float*)(header+112),1); } unsigned short *dim = (unsigned short*)(header+40), dimx=1, dimy=1, dimz=1, dimv=1; if (!dim[0]) cimg::warn("CImg<%s>::load_analyze() : Specified image has zero dimensions.",pixel_type()); if (dim[0]>4) cimg::warn("CImg<%s>::load_analyze() : Number of image dimension is %d, reading only the 4 first dimensions", pixel_type(),dim[0]); if (dim[0]>=1) dimx = dim[1]; if (dim[0]>=2) dimy = dim[2]; if (dim[0]>=3) dimz = dim[3]; if (dim[0]>=4) dimv = dim[4]; float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1; const unsigned short datatype = *(short*)(header+70); if (voxsize) { const float *vsize = (float*)(header+76); voxsize[0] = vsize[1]; voxsize[1] = vsize[2]; voxsize[2] = vsize[3]; } delete[] header; // Read pixel data std::FILE *nfile = file?file:file_header; assign(dimx,dimy,dimz,dimv); switch (datatype) { case 2: { unsigned char *buffer = new unsigned char[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 4: { short *buffer = new short[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 8: { int *buffer = new int[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 16: { float *buffer = new float[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; case 64: { double *buffer = new double[dimx*dimy*dimz*dimv]; cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile); if (endian) cimg::endian_swap(buffer,dimx*dimy*dimz*dimv); cimg_foroff(*this,off) data[off] = (T)(buffer[off]*scalefactor); delete[] buffer; } break; default: cimg::fclose(nfile); throw CImgIOException("CImg<%s>::load_analyze() : File '%s, cannot read images with 'datatype = %d'", pixel_type(),filename,datatype); } cimg::fclose(nfile); return *this; } //! Load a 3D object from a .OFF file (GeomView 3D object files). template static CImg get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImg().load_off(filename,primitives,colors,invert_faces); } template CImg& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { std::FILE *file=cimg::fopen(filename,"r"); unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0; char line[256] = { 0 }; int err; // Skip comments, and read magic string OFF do { err = std::fscanf(file,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) { cimg::fclose(file); throw CImgIOException("CImg<%s>::load_off() : File '%s', keyword 'OFF' not found.",pixel_type(),filename); } do { err = std::fscanf(file,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) { cimg::fclose(file); throw CImgIOException("CImg<%s>::load_off() : File '%s', invalid vertices/primitives numbers.",pixel_type(),filename); } // Read points data assign(nb_points,3); float X = 0, Y = 0, Z = 0; cimg_forX(*this,l) { do { err = std::fscanf(file,"%255[^\n] ",line); } while (!err || (err==1 && line[0]=='#')); if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) { cimg::fclose(file); throw CImgIOException("CImg<%s>::load_off() : File '%s', cannot read point %u/%u.\n",pixel_type(),filename,l+1,nb_points); } (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z; } // Read primitive data primitives.assign(); colors.assign(); bool stopflag = false; while (!stopflag) { float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f; unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0; line[0]='\0'; if ((err = std::fscanf(file,"%u",&prim))!=1) stopflag=true; else { ++nb_read; switch (prim) { case 1: { if ((err = std::fscanf(file,"%u%255[^\n] ",&i0,line))<2) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); primitives.insert(CImg::vector(i0)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 2: { if ((err = std::fscanf(file,"%u%u%255[^\n] ",&i0,&i1,line))<2) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); primitives.insert(CImg::vector(i0,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 3: { if ((err = std::fscanf(file,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); else primitives.insert(CImg::vector(i0,i2,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255))); } } break; case 4: { if ((err = std::fscanf(file,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2,i3)); else primitives.insert(CImg::vector(i0,i3,i2,i1)); colors.insert(CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); } } break; case 5: { if ((err = std::fscanf(file,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i4,i3)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++nb_primitives; } } break; case 6: { if ((err = std::fscanf(file,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4,i5)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i5,i4,i3)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++nb_primitives; } } break; case 7: { if ((err = std::fscanf(file,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i3,i4)); primitives.insert(CImg::vector(i0,i4,i5,i6)); primitives.insert(CImg::vector(i1,i2,i3)); } else { primitives.insert(CImg::vector(i0,i4,i3,i1)); primitives.insert(CImg::vector(i0,i6,i5,i4)); primitives.insert(CImg::vector(i3,i2,i1)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++(++nb_primitives); } } break; case 8: { if ((err = std::fscanf(file,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) { cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u.", pixel_type(),filename,nb_read,nb_primitives); std::fscanf(file,"%*[^\n] "); } else { std::sscanf(line,"%f%f%f",&c0,&c1,&c2); if (invert_faces) { primitives.insert(CImg::vector(i0,i1,i2,i3)); primitives.insert(CImg::vector(i0,i3,i4,i5)); primitives.insert(CImg::vector(i0,i5,i6,i7)); } else { primitives.insert(CImg::vector(i0,i3,i2,i1)); primitives.insert(CImg::vector(i0,i5,i4,i3)); primitives.insert(CImg::vector(i0,i7,i6,i5)); } colors.insert(2,CImg::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255),(tc)(c2*255))); ++(++nb_primitives); } } break; default: cimg::warn("CImg<%s>::load_off() : File '%s', invalid primitive %u/%u (%u vertices).", pixel_type(),filename,nb_read,nb_primitives,prim); std::fscanf(file,"%*[^\n] "); break; } } } cimg::fclose(file); if (primitives.size!=nb_primitives) cimg::warn("CImg<%s>::load_off() : File '%s', read only %u primitives instead of %u as claimed in the header.", pixel_type(),filename,primitives.size,nb_primitives); return *this; } //! Load an image from a DICOM file. static CImg get_load_dicom(const char *const filename) { return CImg().load_dicom(filename); } CImg& load_dicom(const char *const filename) { char command[1024], filetmp[512], body[512]; cimg::fclose(cimg::fopen(filename,"r")); std::FILE *file; do { std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); std::sprintf(command,"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename); cimg::system(command); cimg::filename_split(filetmp,body); std::sprintf(command,"m000-%s.hdr",body); file = std::fopen(command,"rb"); if (!file) { throw CImgIOException("CImg<%s>::load_dicom() : Failed to open image '%s'.\n\n" "Path of 'medcon' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::medcon_path(),filetmp); } else cimg::fclose(file); load_analyze(command); std::remove(command); std::sprintf(command,"m000-%s.img",body); std::remove(command); return *this; } //! Load an image using ImageMagick's convert. static CImg get_load_imagemagick(const char *const filename) { return CImg().load_imagemagick(filename); } CImg& load_imagemagick(const char *const filename) { char command[1024], filetmp[512]; std::FILE *file = 0; do { std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); std::sprintf(command,"%s \"%s\" %s",cimg::imagemagick_path(),filename,filetmp); cimg::system(command,cimg::imagemagick_path()); if (!(file = std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_imagemagick() : Failed to open image '%s'.\n\n" "Path of 'ImageMagick's convert' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::imagemagick_path(),filetmp); } else cimg::fclose(file); load_pnm(filetmp); std::remove(filetmp); return *this; } //! Load an image using GraphicsMagick's convert. static CImg get_load_graphicsmagick(const char *const filename) { return CImg().load_graphicsmagick(filename); } CImg& load_graphicsmagick(const char *const filename) { char command[1024], filetmp[512]; std::FILE *file = 0; do { std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); std::sprintf(command,"%s convert \"%s\" %s",cimg::graphicsmagick_path(),filename,filetmp); cimg::system(command,cimg::graphicsmagick_path()); if (!(file = std::fopen(filetmp,"rb"))) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::load_graphicsmagick() : Failed to open image '%s'.\n\n" "Path of 'GraphicsMagick's gm' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); } else cimg::fclose(file); load_pnm(filetmp); std::remove(filetmp); return *this; } //! Load an image using ImageMagick's or GraphicsMagick's executables. static CImg get_load_other(const char *const filename) { return CImg().load_other(filename); } CImg& load_other(const char *const filename) { const unsigned int odebug = cimg::exception_mode(); cimg::exception_mode() = 0; try { load_magick(filename); } catch (CImgException&) { try { load_imagemagick(filename); } catch (CImgException&) { try { load_graphicsmagick(filename); } catch (CImgException&) { assign(); } } } cimg::exception_mode() = odebug; if (is_empty()) throw CImgIOException("CImg<%s>::load_other() : Failed to open image '%s'.\n" "Check you have either the ImageMagick or GraphicsMagick package installed.", pixel_type(),filename); return *this; } //! Load an image from a PAR-REC (Philips) file. static CImg get_load_parrec(const char *const filename, const char axis='v', const char align='p') { return CImg().load_parrec(filename,axis,align); } CImg& load_parrec(const char *const filename, const char axis='v', const char align='p') { CImgList list; list.load_parrec(filename); if (list.size==1) return list[0].assign_to(*this); return assign(list.get_append(axis,align)); } //! Load an image (list) from a .cimg file. static CImg get_load_cimg(std::FILE *const file, const char axis='z', const char align='p') { return CImg().load_cimg(file,axis,align); } CImg& load_cimg(std::FILE *const file, const char axis='z', const char align='p') { CImgList list; list.load_cimg(file); if (list.size==1) return list[0].assign_to(*this); return assign(list.get_append(axis,align)); } //! Load an image (list) from a .cimg file. static CImg get_load_cimg(const char *const filename, const char axis='z', const char align='p') { return CImg().load_cimg(filename,axis,align); } CImg& load_cimg(const char *const filename, const char axis='z', const char align='p') { CImgList list; list.load_cimg(filename); if (list.size==1) return list[0].assign_to(*this); return assign(list.get_append(axis,align)); } //! Load a sub-image (list) from a .cimg file. static CImg get_load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { return CImg().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); } CImg& load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { CImgList list; list.load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); if (list.size==1) return list[0].assign_to(*this); return assign(list.get_append(axis,align)); } //! Load a sub-image (list) from a .cimg file. static CImg get_load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { return CImg().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1,axis,align); } CImg& load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1, const char axis='z', const char align='p') { CImgList list; list.load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); if (list.size==1) return list[0].assign_to(*this); return assign(list.get_append(axis,align)); } //! Load an image sequence from a YUV file. static CImg get_load_yuv(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb = false, const char axis='z', const char align='p') { return CImgList().load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb).get_append(axis,align); } CImg& load_yuv(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb = false, const char axis='z', const char align='p') { return get_load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb,axis,align).assign_to(*this); } //! Load an image sequence from a YUV file. static CImg get_load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb = false, const char axis='z', const char align='p') { return CImgList().load_yuv(filename,sizex,sizey,first_frame,last_frame,yuv2rgb).get_append(axis,align); } CImg& load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb = false, const char axis='z', const char align='p') { return get_load_yuv(0,filename,sizex,sizey,first_frame,last_frame,yuv2rgb,axis,align).assign_to(*this); } //! Save the image as a file. /** The used file format is defined by the file extension in the filename \p filename.\n Parameter \p number can be used to add a 6-digit number to the filename before saving.\n If \p normalize is true, a normalized version of the image (between [0,255]) is saved. **/ const CImg& save(const char *const filename, const int number=-1) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); const char *ext = cimg::filename_split(filename); char nfilename[1024]; const char *const fn = (number>=0)?cimg::filename_number(filename,number,6,nfilename):filename; #ifdef cimg_save_plugin cimg_save_plugin(fn); #endif if (!cimg::strncasecmp(ext,"asc",3)) return save_ascii(fn); if (!cimg::strncasecmp(ext,"dlm",3) || !cimg::strncasecmp(ext,"txt",3)) return save_dlm(fn); if (!cimg::strncasecmp(ext,"inr",3)) return save_inr(fn); if (!cimg::strncasecmp(ext,"hdr",3) || !cimg::strncasecmp(ext,"nii",3)) return save_analyze(fn); if (!cimg::strncasecmp(ext,"dcm",3)) return save_dicom(fn); if (!cimg::strncasecmp(ext,"pan",3)) return save_pandore(fn); if (!cimg::strncasecmp(ext,"bmp",3)) return save_bmp(fn); if (!cimg::strncasecmp(ext,"png",3)) return save_png(fn); if (!cimg::strncasecmp(ext,"tif",3)) return save_tiff(fn); if (!cimg::strncasecmp(ext,"jpg",3) || !cimg::strncasecmp(ext,"jpeg",4)) return save_jpeg(fn); if (!cimg::strncasecmp(ext,"rgba",4)) return save_rgba(fn); if (!cimg::strncasecmp(ext,"rgb",3)) return save_rgb(fn); if (!cimg::strncasecmp(ext,"raw",3)) return save_raw(fn); if (!cimg::strncasecmp(ext,"cimg",4) || ext[0]=='\0') return save_cimg(fn); if (!cimg::strncasecmp(ext,"pgm",3) || !cimg::strncasecmp(ext,"ppm",3) || !cimg::strncasecmp(ext,"pnm",3)) return save_pnm(fn); if (!cimg::strncasecmp(ext,"yuv",3)) return save_yuv(fn,true); return save_other(fn); } //! Save the image as an ASCII file (ASCII Raw + simple header). const CImg& save_ascii(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_ascii() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); std::fprintf(nfile,"%u %u %u %u\n",width,height,depth,dim); const T* ptrs = data; cimg_forYZV(*this,y,z,v) { cimg_forX(*this,x) std::fprintf(nfile,"%g ",(double)*(ptrs++)); std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as an ASCII file (ASCII Raw + simple header). const CImg& save_ascii(const char *const filename) const { return save_ascii(0,filename); } //! Save the image as a DLM file. const CImg& save_dlm(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is volumetric. Pixel values along Z will be unrolled (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (dim>1) cimg::warn("CImg<%s>::save_dlm() : Instance image (%u,%u,%u,%u,%p) is multispectral. Pixel values along V will be unrolled (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); const T* ptrs = data; cimg_forYZV(*this,y,z,v) { cimg_forX(*this,x) std::fprintf(nfile,"%g%s",(double)*(ptrs++),(x==dimx()-1)?"":","); std::fputc('\n',nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a DLM file. const CImg& save_dlm(const char *const filename) const { return save_dlm(0,filename); } //! Save the image as a PNM file. const CImg& save_pnm(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); double stmin, stmax = (double)maxmin(stmin); if (depth>1) cimg::warn("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (dim>3) cimg::warn("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (stmin<0 || stmax>65535) cimg::warn("CImg<%s>::save_pnm() : Instance image (%u,%u,%u,%u,%p) has pixel values in [%g,%g]. Probable type overflow (file '%s').",pixel_type(),width,height,depth,dim,data,stmin,stmax,filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const T *ptrR = ptr(0,0,0,0), *ptrG = (dim>=2)?ptr(0,0,0,1):ptrR, *ptrB = (dim>=3)?ptr(0,0,0,2):ptrR; const unsigned int buf_size = width*height*(dim==1?1:3); std::fprintf(nfile,"P%c\n# CREATOR: CImg : Original size=%ux%ux%ux%u\n%u %u\n%u\n", (dim==1?'5':'6'),width,height,depth,dim,width,height,stmax<256?255:65535); switch(dim) { case 1: { if (stmax<256) // Binary PGM 8 bits { unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) *(xptrd++) = (unsigned char)*(ptrR++); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } else // Binary PGM 16 bits { unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) *(xptrd++) = (unsigned short)*(ptrR++); if (!cimg::endian()) cimg::endian_swap(ptrd,buf_size); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } } break; default: { if (stmax<256) // Binary PPM 8 bits { unsigned char *ptrd = new unsigned char[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned char)*(ptrR++); *(xptrd++) = (unsigned char)*(ptrG++); *(xptrd++) = (unsigned char)*(ptrB++); } cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } else // Binary PPM 16 bits { unsigned short *ptrd = new unsigned short[buf_size], *xptrd = ptrd; cimg_forXY(*this,x,y) { *(xptrd++) = (unsigned short)*(ptrR++); *(xptrd++) = (unsigned short)*(ptrG++); *(xptrd++) = (unsigned short)*(ptrB++); } if (!cimg::endian()) cimg::endian_swap(ptrd,buf_size); cimg::fwrite(ptrd,buf_size,nfile); delete[] ptrd; } } break; } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a PNM file. const CImg& save_pnm(const char *const filename) const { return save_pnm(0,filename); } //! Save an image as a Dicom file (need '(X)Medcon' : http://xmedcon.sourceforge.net ) const CImg& save_dicom(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_dicom() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save_dicom() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024], filetmp[512], body[512]; std::FILE *file; do { std::sprintf(filetmp,"%s.hdr",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); save_analyze(filetmp); std::sprintf(command,"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp); cimg::system(command); std::remove(filetmp); cimg::filename_split(filetmp,body); std::sprintf(filetmp,"%s.img",body); std::remove(filetmp); std::sprintf(command,"m000-%s",filename); file = std::fopen(command,"rb"); if (!file) { cimg::fclose(cimg::fopen(filename,"r")); throw CImgIOException("CImg<%s>::save_dicom() : Failed to save image '%s'.\n\n" "Path of 'medcon' : \"%s\"\n" "Path of temporary filename : \"%s\"", pixel_type(),filename,cimg::medcon_path(),filetmp); } else cimg::fclose(file); std::rename(command,filename); return *this; } //! Save the image as an ANALYZE7.5 or NIFTI file. const CImg& save_analyze(const char *const filename, const float *const voxsize=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_analyze() : File '%s', instance image (%u,%u,%u,%u,%p) is empty.", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save_analyze() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); std::FILE *file; char header[348], hname[1024], iname[1024]; const char *ext = cimg::filename_split(filename); short datatype=-1; std::memset(header,0,348); if (!ext[0]) { std::sprintf(hname,"%s.hdr",filename); std::sprintf(iname,"%s.img",filename); } if (!cimg::strncasecmp(ext,"hdr",3)) { std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(iname+cimg::strlen(iname)-3,"img"); } if (!cimg::strncasecmp(ext,"img",3)) { std::strcpy(hname,filename); std::strcpy(iname,filename); std::sprintf(hname+cimg::strlen(iname)-3,"hdr"); } if (!cimg::strncasecmp(ext,"nii",3)) { std::strcpy(hname,filename); iname[0] = 0; } ((int*)(header))[0] = 348; std::sprintf(header+4,"CImg"); std::sprintf(header+14," "); ((short*)(header+36))[0] = 4096; ((char*)(header+38))[0] = 114; ((short*)(header+40))[0] = 4; ((short*)(header+40))[1] = width; ((short*)(header+40))[2] = height; ((short*)(header+40))[3] = depth; ((short*)(header+40))[4] = dim; if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2; if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4; if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4; if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8; if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16; if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64; if (datatype<0) throw CImgIOException("CImg<%s>::save_analyze() : Cannot save image '%s' since pixel type (%s)" "is not handled in Analyze7.5 specifications.\n", pixel_type(),filename,pixel_type()); ((short*)(header+70))[0] = datatype; ((short*)(header+72))[0] = sizeof(T); ((float*)(header+112))[0] = 1; ((float*)(header+76))[0] = 0; if (voxsize) { ((float*)(header+76))[1] = voxsize[0]; ((float*)(header+76))[2] = voxsize[1]; ((float*)(header+76))[3] = voxsize[2]; } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1; file = cimg::fopen(hname,"wb"); cimg::fwrite(header,348,file); if (iname[0]) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); } cimg::fwrite(data,size(),file); cimg::fclose(file); return *this; } //! Save the image as a .cimg file. const CImg& save_cimg(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_cimg() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); CImgList tmp(1); tmp[0].width = width; tmp[0].height = height; tmp[0].depth = depth; tmp[0].dim = dim; tmp[0].data = data; tmp.save_cimg(file,filename); tmp[0].width = tmp[0].height = tmp[0].depth = tmp[0].dim = 0; tmp[0].data = 0; return *this; } //! Save the image as a .cimg file. const CImg& save_cimg(const char *const filename) const { return save_cimg(0,filename); } //! Insert the image into an existing .cimg file, at specified coordinates. const CImg& save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { CImgList tmp(1); tmp[0].width = width; tmp[0].height = height; tmp[0].depth = depth; tmp[0].dim = dim; tmp[0].data = data; tmp.save_cimg(file,n0,x0,y0,z0,v0); tmp[0].width = tmp[0].height = tmp[0].depth = tmp[0].dim = 0; tmp[0].data = 0; return *this; } //! Insert the image into an existing .cimg file, at specified coordinates. const CImg& save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { CImgList tmp(1); tmp[0].width = width; tmp[0].height = height; tmp[0].depth = depth; tmp[0].dim = dim; tmp[0].data = data; tmp.save_cimg(filename,n0,x0,y0,z0,v0); tmp[0].width = tmp[0].height = tmp[0].depth = tmp[0].dim = 0; tmp[0].data = 0; return *this; } //! Save an empty .cimg file with specified dimensions. static void save_empty_cimg(std::FILE *const file, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { return CImgList::save_empty_cimg(file,1,dx,dy,dz,dv); } //! Save an empty .cimg file with specified dimensions. static void save_empty_cimg(const char *const filename, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { return CImgList::save_empty_cimg(filename,1,dx,dy,dz,dv); } //! Save the image as a RAW file const CImg& save_raw(std::FILE *const file, const char *const filename=0, const bool multiplexed=false) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_raw() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); if (!multiplexed) cimg::fwrite(data,size(),nfile); else { CImg buf(dim); cimg_forXYZ(*this,x,y,z) { cimg_forV(*this,k) buf[k] = (*this)(x,y,z,k); cimg::fwrite(buf.data,dim,nfile); } } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a RAW file const CImg& save_raw(const char *const filename=0, const bool multiplexed=false) const { return save_raw(0,filename,multiplexed); } //! Save the image using ImageMagick's convert. /** Function that saves the image for other file formats that are not natively handled by CImg, using the tool 'convert' from the ImageMagick package.\n This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). You need to install the ImageMagick package in order to get this function working properly (see http://www.imagemagick.org ). **/ const CImg& save_imagemagick(const char *const filename, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_imagemagick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s')", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save_imagemagick() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024],filetmp[512]; std::FILE *file; do { if (dim==1) std::sprintf(filetmp,"%s%s%s.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); else std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); save_pnm(filetmp); std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename); cimg::system(command); file = std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImg<%s>::save_imagemagick() : Failed to save image '%s'.\n\n" "Path of 'convert' : \"%s\"\n" "Path of temporary filename : \"%s\"\n", pixel_type(),filename,cimg::imagemagick_path(),filetmp); if (file) cimg::fclose(file); std::remove(filetmp); return *this; } //! Save the image using GraphicsMagick's gm. /** Function that saves the image for other file formats that are not natively handled by CImg, using the tool 'gm' from the GraphicsMagick package.\n This is the case for all compressed image formats (GIF,PNG,JPG,TIF,...). You need to install the GraphicsMagick package in order to get this function working properly (see http://www.graphicsmagick.org ). **/ const CImg& save_graphicsmagick(const char *const filename, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_graphicsmagick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s')", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save_graphicsmagick() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); char command[1024],filetmp[512]; std::FILE *file; do { if (dim==1) std::sprintf(filetmp,"%s%s%s.pgm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); else std::sprintf(filetmp,"%s%s%s.ppm",cimg::temporary_path(),cimg_OS==2?"\\":"/",cimg::filenamerand()); if ((file=std::fopen(filetmp,"rb"))!=0) std::fclose(file); } while (file); save_pnm(filetmp); std::sprintf(command,"%s -quality %u%% %s \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename); cimg::system(command); file = std::fopen(filename,"rb"); if (!file) throw CImgIOException("CImg<%s>::save_graphicsmagick() : Failed to save image '%s'.\n\n" "Path of 'gm' : \"%s\"\n" "Path of temporary filename : \"%s\"\n", pixel_type(),filename,cimg::graphicsmagick_path(),filetmp); if (file) cimg::fclose(file); std::remove(filetmp); return *this; } const CImg& save_other(const char *const filename, const unsigned int quality=100) const { const unsigned int odebug = cimg::exception_mode(); bool is_saved = true; cimg::exception_mode() = 0; try { save_magick(filename); } catch (CImgException&) { try { save_imagemagick(filename,quality); } catch (CImgException&) { try { save_graphicsmagick(filename,quality); } catch (CImgException&) { is_saved = false; } } } cimg::exception_mode() = odebug; if (!is_saved) throw CImgIOException("CImg<%s>::save_other() : File '%s' cannot be saved.\n" "Check you have either the ImageMagick or GraphicsMagick package installed.", pixel_type(),filename); return *this; } //! Save the image as an INRIMAGE-4 file. const CImg& save_inr(std::FILE *const file, const char *const filename=0, const float *const voxsize=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!filename) throw CImgArgumentException("CImg<%s>::save_inr() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); int inrpixsize=-1; const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } if (!cimg::strcasecmp(pixel_type(),"char")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; } if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } if (!cimg::strcasecmp(pixel_type(),"short")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; } if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"int")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"float")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; } if (!cimg::strcasecmp(pixel_type(),"double")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; } if (inrpixsize<=0) throw CImgIOException("CImg<%s>::save_inr() : Don't know how to save images of '%s'",pixel_type(),pixel_type()); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); char header[257]; int err = std::sprintf(header,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",width,height,depth,dim); if (voxsize) err += std::sprintf(header+err,"VX=%g\nVY=%g\nVZ=%g\n",voxsize[0],voxsize[1],voxsize[2]); err += std::sprintf(header+err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endian()?"sun":"decm"); std::memset(header+err,'\n',252-err); std::memcpy(header+252,"##}\n",4); cimg::fwrite(header,256,nfile); cimg_forXYZ(*this,x,y,z) cimg_forV(*this,k) cimg::fwrite(&((*this)(x,y,z,k)),1,nfile); if (!file) cimg::fclose(nfile); return *this; } //! Save the image as an INRIMAGE-4 file. const CImg& save_inr(const char *const filename, const float *const voxsize=0) const { return save_inr(0,filename,voxsize); } #define cimg_save_pandore_case(sy,sz,sv,stype,id) \ if (!saved && (sy?(sy==height):true) && (sz?(sz==depth):true) && (sv?(sv==dim):true) && !strcmp(stype,pixel_type())) { \ unsigned int *iheader = (unsigned int*)(header+12); \ nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \ cimg::fwrite(header,36,nfile); \ cimg::fwrite(dims,nbdims,nfile); \ if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \ unsigned char *buffer = new unsigned char[size()]; \ const T *ptrs = data; \ cimg_foroff(*this,off) *(buffer++) = (unsigned char)(*(ptrs++)); \ buffer-=size(); \ cimg::fwrite(buffer,size(),nfile); \ delete[] buffer; \ } \ if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \ unsigned long *buffer = new unsigned long[size()]; \ const T *ptrs = data; \ cimg_foroff(*this,off) *(buffer++) = (long)(*(ptrs++)); \ buffer-=size(); \ cimg::fwrite(buffer,size(),nfile); \ delete[] buffer; \ } \ if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \ float *buffer = new float[size()]; \ const T *ptrs = data; \ cimg_foroff(*this,off) *(buffer++) = (float)(*(ptrs++)); \ buffer-=size(); \ cimg::fwrite(buffer,size(),nfile); \ delete[] buffer; \ } \ saved = true; \ } unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace=0) const { unsigned int nbdims = 0; if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = width; nbdims = 2; } if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = height; dims[2] = width; nbdims=3; } if (id==8 || id==9 || id==10) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = height; dims[2] = width; dims[3] = colorspace; nbdims = 4; } if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = depth; dims[2] = height; dims[3] = width; dims[4] = colorspace; nbdims = 5; } if (id==22 || id==23 || id==25) { dims[0] = dim; dims[1] = width; nbdims = 2; } if (id==26 || id==27 || id==29) { dims[0] = dim; dims[1] = height; dims[2] = width; nbdims=3; } if (id==30 || id==31 || id==33) { dims[0] = dim; dims[1] = depth; dims[2] = height; dims[3] = width; nbdims = 4; } return nbdims; } //! Save the image as a PANDORE-5 file. const CImg& save_pandore(std::FILE *const file, const char *const filename=0, const unsigned int colorspace=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_pandore() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0, 0,0,0,0, 'C','I','m','g',0,0,0,0,0, 'N','o',' ','d','a','t','e',0,0,0, 0 }; unsigned int nbdims,dims[5]; bool saved=false; cimg_save_pandore_case(1,1,1,"unsigned char",2); cimg_save_pandore_case(1,1,1,"char",3); cimg_save_pandore_case(1,1,1,"short",3); cimg_save_pandore_case(1,1,1,"unsigned short",3); cimg_save_pandore_case(1,1,1,"unsigned int",3); cimg_save_pandore_case(1,1,1,"int",3); cimg_save_pandore_case(1,1,1,"unsigned long",4); cimg_save_pandore_case(1,1,1,"long",3); cimg_save_pandore_case(1,1,1,"float",4); cimg_save_pandore_case(1,1,1,"double",4); cimg_save_pandore_case(0,1,1,"unsigned char",5); cimg_save_pandore_case(0,1,1,"char",6); cimg_save_pandore_case(0,1,1,"short",6); cimg_save_pandore_case(0,1,1,"unsigned short",6); cimg_save_pandore_case(0,1,1,"unsigned int",6); cimg_save_pandore_case(0,1,1,"int",6); cimg_save_pandore_case(0,1,1,"unsigned long",7); cimg_save_pandore_case(0,1,1,"long",6); cimg_save_pandore_case(0,1,1,"float",7); cimg_save_pandore_case(0,1,1,"double",7); cimg_save_pandore_case(0,0,1,"unsigned char",8); cimg_save_pandore_case(0,0,1,"char",9); cimg_save_pandore_case(0,0,1,"short",9); cimg_save_pandore_case(0,0,1,"unsigned short",9); cimg_save_pandore_case(0,0,1,"unsigned int",9); cimg_save_pandore_case(0,0,1,"int",9); cimg_save_pandore_case(0,0,1,"unsigned long",10); cimg_save_pandore_case(0,0,1,"long",9); cimg_save_pandore_case(0,0,1,"float",10); cimg_save_pandore_case(0,0,1,"double",10); cimg_save_pandore_case(0,1,3,"unsigned char",16); cimg_save_pandore_case(0,1,3,"char",17); cimg_save_pandore_case(0,1,3,"short",17); cimg_save_pandore_case(0,1,3,"unsigned short",17); cimg_save_pandore_case(0,1,3,"unsigned int",17); cimg_save_pandore_case(0,1,3,"int",17); cimg_save_pandore_case(0,1,3,"unsigned long",18); cimg_save_pandore_case(0,1,3,"long",17); cimg_save_pandore_case(0,1,3,"float",18); cimg_save_pandore_case(0,1,3,"double",18); cimg_save_pandore_case(0,0,3,"unsigned char",19); cimg_save_pandore_case(0,0,3,"char",20); cimg_save_pandore_case(0,0,3,"short",20); cimg_save_pandore_case(0,0,3,"unsigned short",20); cimg_save_pandore_case(0,0,3,"unsigned int",20); cimg_save_pandore_case(0,0,3,"int",20); cimg_save_pandore_case(0,0,3,"unsigned long",21); cimg_save_pandore_case(0,0,3,"long",20); cimg_save_pandore_case(0,0,3,"float",21); cimg_save_pandore_case(0,0,3,"double",21); cimg_save_pandore_case(1,1,0,"unsigned char",22); cimg_save_pandore_case(1,1,0,"char",23); cimg_save_pandore_case(1,1,0,"short",23); cimg_save_pandore_case(1,1,0,"unsigned short",23); cimg_save_pandore_case(1,1,0,"unsigned int",23); cimg_save_pandore_case(1,1,0,"int",23); cimg_save_pandore_case(1,1,0,"unsigned long",25); cimg_save_pandore_case(1,1,0,"long",23); cimg_save_pandore_case(1,1,0,"float",25); cimg_save_pandore_case(1,1,0,"double",25); cimg_save_pandore_case(0,1,0,"unsigned char",26); cimg_save_pandore_case(0,1,0,"char",27); cimg_save_pandore_case(0,1,0,"short",27); cimg_save_pandore_case(0,1,0,"unsigned short",27); cimg_save_pandore_case(0,1,0,"unsigned int",27); cimg_save_pandore_case(0,1,0,"int",27); cimg_save_pandore_case(0,1,0,"unsigned long",29); cimg_save_pandore_case(0,1,0,"long",27); cimg_save_pandore_case(0,1,0,"float",29); cimg_save_pandore_case(0,1,0,"double",29); cimg_save_pandore_case(0,0,0,"unsigned char",30); cimg_save_pandore_case(0,0,0,"char",31); cimg_save_pandore_case(0,0,0,"short",31); cimg_save_pandore_case(0,0,0,"unsigned short",31); cimg_save_pandore_case(0,0,0,"unsigned int",31); cimg_save_pandore_case(0,0,0,"int",31); cimg_save_pandore_case(0,0,0,"unsigned long",33); cimg_save_pandore_case(0,0,0,"long",31); cimg_save_pandore_case(0,0,0,"float",33); cimg_save_pandore_case(0,0,0,"double",33); if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a PANDORE-5 file. const CImg& save_pandore(const char *const filename=0, const unsigned int colorspace=0) const { return save_pandore(0,filename,colorspace); } //! Save the image as a YUV video sequence file const CImg& save_yuv(std::FILE *const file, const char *const filename=0, const bool rgb2yuv=true) const { CImgList(*this).save_yuv(file,filename,rgb2yuv); return *this; } //! Save the image as a YUV video sequence file const CImg& save_yuv(const char *const filename, const bool rgb2yuv=true) const { return save_yuv(0,filename,rgb2yuv); } //! Save the image as a BMP file const CImg& save_bmp(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (dim>3) cimg::warn("CImg<%s>::save_bmp() : Instance image (%u,%u,%u,%u,%p) is multispectral. Only the three first channels will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); unsigned char header[54] = { 0 }, align_buf[4] = { 0 }; const unsigned int align = (4-(3*width)%4)%4, buf_size = (3*width+align)*dimy(), file_size = 54+buf_size; header[0] = 'B'; header[1] = 'M'; header[0x02] = file_size&0xFF; header[0x03] = (file_size>>8)&0xFF; header[0x04] = (file_size>>16)&0xFF; header[0x05] = (file_size>>24)&0xFF; header[0x0A] = 0x36; header[0x0E] = 0x28; header[0x12] = width&0xFF; header[0x13] = (width>>8)&0xFF; header[0x14] = (width>>16)&0xFF; header[0x15] = (width>>24)&0xFF; header[0x16] = height&0xFF; header[0x17] = (height>>8)&0xFF; header[0x18] = (height>>16)&0xFF; header[0x19] = (height>>24)&0xFF; header[0x1A] = 1; header[0x1B] = 0; header[0x1C] = 24; header[0x1D] = 0; header[0x22] = buf_size&0xFF; header[0x23] = (buf_size>>8)&0xFF; header[0x24] = (buf_size>>16)&0xFF; header[0x25] = (buf_size>>24)&0xFF; header[0x27] = 0x1; header[0x2B] = 0x1; cimg::fwrite(header,54,nfile); const T *pR = ptr(0,height-1,0,0), *pG = (dim>=2)?ptr(0,height-1,0,1):pR, *pB = (dim>=3)?ptr(0,height-1,0,2):pR; cimg_forY(*this,y) { cimg_forX(*this,x) { std::fputc((unsigned char)(*(pB++)),nfile); std::fputc((unsigned char)(*(pG++)),nfile); std::fputc((unsigned char)(*(pR++)),nfile); } cimg::fwrite(align_buf,align,nfile); pR-=2*width; pG-=2*width; pB-=2*width; } if (!file) cimg::fclose(nfile); return *this; } //! Save the image as a BMP file const CImg& save_bmp(const char *const filename) const { return save_bmp(0,filename); } //! Save an image to a PNG file. // Most of this function has been written by Eric Fausett /** \param filename = name of the png image file to save \return *this \note The png format specifies a variety of possible data formats. Grey scale, Grey scale with Alpha, RGB color, RGB color with Alpha, and Palletized color are supported. Per channel bit depths of 1, 2, 4, 8, and 16 are natively supported. The type of file saved depends on the number of channels in the CImg file. If there is 4 or more channels, the image will be saved as an RGB color with Alpha image using the bottom 4 channels. If there are 3 channels, the saved image will be an RGB color image. If 2 channels then the image saved will be Grey scale with Alpha, and if 1 channel will be saved as a Grey scale image. **/ const CImg& save_png(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!filename) throw CImgArgumentException("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_png() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); #ifndef cimg_use_png if (!file) return save_other(filename); else throw CImgIOException("CImg<%s>::save_png() : Cannot save a PNG image in a *FILE output. Use libpng instead.", pixel_type()); #else std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); // Setup PNG structures for write png_voidp user_error_ptr = 0; png_error_ptr user_error_fn = 0, user_warning_fn = 0; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, user_error_ptr, user_error_fn, user_warning_fn); if(!png_ptr) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'png_ptr' data structure.", pixel_type(),filename?filename:"(unknown)"); } png_infop info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { png_destroy_write_struct(&png_ptr,(png_infopp)0); if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', error when initializing 'info_ptr' data structure.", pixel_type(),filename?filename:"(unknown)"); } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", pixel_type(),filename?filename:"(unknown)"); } png_init_io(png_ptr, nfile); png_uint_32 width = dimx(); png_uint_32 height = dimy(); float vmin, vmax = (float)maxmin(vmin); const int bit_depth = (vmin<0 || vmax>=256)?16:8; int color_type; switch (dimv()) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; default: color_type = PNG_COLOR_TYPE_RGB_ALPHA; } const int interlace_type = PNG_INTERLACE_NONE; const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT; const int filter_method = PNG_FILTER_TYPE_DEFAULT; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, compression_type, filter_method); png_write_info(png_ptr, info_ptr); const int byte_depth = bit_depth>>3; const int numChan = dimv()>4?4:dimv(); const int pixel_bit_depth_flag = numChan * (bit_depth-1); // Allocate Memory for Image Save and Fill pixel data png_bytep *imgData = new png_byte*[height]; for (unsigned int row=0; row(imgData[y]); cimg_forX(*this,x) *(ptrs++) = (unsigned short)*(pC0++); } } break; case 30: // Gray w/ Alpha 16-bit { const T *pC1 = ptr(0,0,0,1); cimg_forY(*this,y) { unsigned short *ptrs = reinterpret_cast(imgData[y]); cimg_forX(*this,x) { *(ptrs++) = (unsigned short)*(pC0++); *(ptrs++) = (unsigned short)*(pC1++); } } } break; case 45: // RGB 16-bit { const T *pC1 = ptr(0,0,0,1); const T *pC2 = ptr(0,0,0,2); cimg_forY(*this,y) { unsigned short *ptrs = reinterpret_cast(imgData[y]); cimg_forX(*this,x) { *(ptrs++) = (unsigned short)*(pC0++); *(ptrs++) = (unsigned short)*(pC1++); *(ptrs++) = (unsigned short)*(pC2++); } } } break; case 60: // RGB w/ Alpha 16-bit { const T *pC1 = ptr(0,0,0,1); const T *pC2 = ptr(0,0,0,2); const T *pC3 = ptr(0,0,0,3); cimg_forY(*this,y) { unsigned short *ptrs = reinterpret_cast(imgData[y]); cimg_forX(*this,x) { *(ptrs++) = (unsigned short)*(pC0++); *(ptrs++) = (unsigned short)*(pC1++); *(ptrs++) = (unsigned short)*(pC2++); *(ptrs++) = (unsigned short)*(pC3++); } } } break; default: if (!file) cimg::fclose(nfile); throw CImgIOException("CImg<%s>::save_png() : File '%s', unknown fatal error.", pixel_type(),filename?filename:"(unknown)"); break; } png_write_image(png_ptr, imgData); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); // Deallocate Image Write Memory for (unsigned int n=0; n::save_tiff() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save_tiff() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_tiff uint32 rowsperstrip = (uint32) -1; uint16 spp = dimv(), bpp = sizeof(T)*8; uint16 photometric; if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB; else photometric = PHOTOMETRIC_MINISBLACK; uint16 compression = COMPRESSION_NONE; TIFF *out; out = TIFFOpen(filename,"w"); if (out) { for (unsigned int dir=0; dirheight?height-row:rowsperstrip); tstrip_t strip = TIFFComputeStrip(out, row, 0); tsize_t i = 0; for (unsigned int rr=0; rr::save_tiff() : File '%s', an error occure while writing a strip.", pixel_type(),filename?filename:"(FILE*)"); } } _TIFFfree(buf); } TIFFWriteDirectory(out); } TIFFClose(out); } else throw CImgException("CImg<%s>::save_tiff() : File '%s', error while writing tiff file.", pixel_type(),filename); #else return save_other(filename); #endif return *this; } //! Save a file in JPEG format. const CImg& save_jpeg(std::FILE *const file, const char *const filename=0, const unsigned int quality=100) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p), specified filename is (null).", pixel_type(),width,height,depth,dim,data); if (depth>1) cimg::warn("CImg<%s>::save_jpeg() : Instance image (%u,%u,%u,%u,%p) is volumetric. Only the first slice will be saved (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); #ifndef cimg_use_jpeg if (!file) return save_other(filename,quality); else throw CImgIOException("CImg<%s>::save_jpeg() : Cannot save a JPEG image in a *FILE output. Use libjpeg instead.", pixel_type()); #else // Fill pixel buffer unsigned char *buf; unsigned int dimbuf = 0; J_COLOR_SPACE colortype = JCS_RGB; switch (dim) { case 1: // Greyscale images { unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=1)]; colortype = JCS_GRAYSCALE; const T *ptr_g = data; cimg_forXY(*this,x,y) *(buf2++) = (unsigned char)*(ptr_g++); } break; case 2: case 3: // RGB images { unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=3)]; const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,dim>2?2:0); colortype = JCS_RGB; cimg_forXY(*this,x,y) { *(buf2++) = (unsigned char)*(ptr_r++); *(buf2++) = (unsigned char)*(ptr_g++); *(buf2++) = (unsigned char)*(ptr_b++); } } break; default: // YCMYK images { unsigned char *buf2 = buf = new unsigned char[width*height*(dimbuf=4)]; const T *ptr_r = ptr(0,0,0,0), *ptr_g = ptr(0,0,0,1), *ptr_b = ptr(0,0,0,2), *ptr_a = ptr(0,0,0,3); colortype = JCS_CMYK; cimg_forXY(*this,x,y) { *(buf2++) = (unsigned char)*(ptr_r++); *(buf2++) = (unsigned char)*(ptr_g++); *(buf2++) = (unsigned char)*(ptr_b++); *(buf2++) = (unsigned char)*(ptr_a++); } } break; } // Call libjpeg functions struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); jpeg_stdio_dest(&cinfo,nfile); cinfo.image_width = width; cinfo.image_height = height; cinfo.input_components = dimbuf; cinfo.in_color_space = colortype; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE); jpeg_start_compress(&cinfo,TRUE); const unsigned int row_stride = width*dimbuf; JSAMPROW row_pointer[1]; while (cinfo.next_scanline < cinfo.image_height) { row_pointer[0] = &buf[cinfo.next_scanline*row_stride]; jpeg_write_scanlines(&cinfo,row_pointer,1); } jpeg_finish_compress(&cinfo); delete[] buf; if (!file) cimg::fclose(nfile); jpeg_destroy_compress(&cinfo); return *this; #endif } //! Save a file in JPEG format. const CImg& save_jpeg(const char *const filename, const unsigned int quality=100) const { return save_jpeg(0,filename,quality); } //! Save the image using built-in ImageMagick++ library const CImg& save_magick(const char *const filename) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data); if (!filename) throw CImgArgumentException("CImg<%s>::save_magick() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); #ifdef cimg_use_magick Magick::Image image(Magick::Geometry(width,height),"black"); image.type(Magick::TrueColorType); const T *rdata = ptr(0,0,0,0), *gdata = dim>1?ptr(0,0,0,1):rdata, *bdata = dim>2?ptr(0,0,0,2):gdata; Magick::PixelPacket *pixels = image.getPixels(0,0,width,height); for (unsigned int off = width*height; off; --off) { pixels->red = Magick::Color::scaleDoubleToQuantum(*(rdata++)/255.0); pixels->green = Magick::Color::scaleDoubleToQuantum(*(gdata++)/255.0); pixels->blue = Magick::Color::scaleDoubleToQuantum(*(bdata++)/255.0); ++pixels; } image.syncPixels(); image.write(filename); #else throw CImgIOException("CImg<%s>::save_magick() : File '%s', Magick++ library has not been linked.", pixel_type(),filename); #endif return *this; } //! Save the image as a RGBA file const CImg& save_rgba(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (dim!=4) cimg::warn("CImg<%s>::save_rgba() : Instance image (%u,%u,%u,%u,%p) has not exactly 4 channels (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int wh = width*height; unsigned char *buffer = new unsigned char[4*wh], *nbuffer=buffer; const T *ptr1 = ptr(0,0,0,0), *ptr2 = dim>1?ptr(0,0,0,1):ptr1, *ptr3 = dim>2?ptr(0,0,0,2):ptr1, *ptr4 = dim>3?ptr(0,0,0,3):0; for (unsigned int k=0; k::save_rgb() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p), specified file is (null).", pixel_type(),width,height,depth,dim,data); if (dim!=3) cimg::warn("CImg<%s>::save_rgb() : Instance image (%u,%u,%u,%u,%p) has not exactly 3 channels (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int wh = width*height; unsigned char *buffer = new unsigned char[3*wh], *nbuffer=buffer; const T *ptr1 = ptr(0,0,0,0), *ptr2 = dim>1?ptr(0,0,0,1):ptr1, *ptr3 = dim>2?ptr(0,0,0,2):ptr1; for (unsigned int k=0; k const CImg& save_off(std::FILE *const file, const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { if (is_empty()) throw CImgInstanceException("CImg<%s>::save_off() : Instance image (%u,%u,%u,%u,%p) is empty (file '%s').", pixel_type(),width,height,depth,dim,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_off() : Specified filename is (null).",pixel_type()); if (height<3) return get_resize(-100,3,1,1,0).save_off(file,filename,primitives,colors,invert_faces); std::FILE *const nfile = file?file:cimg::fopen(filename,"w"); std::fprintf(nfile,"OFF\n%u %u %u\n",width,primitives.size,3*primitives.size); cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2))); cimglist_for(primitives,l) { const unsigned int prim = primitives[l].size(); const bool textured = (prim>4); const CImg& color = colors[l]; const unsigned int s = textured?color.dimv():color.size(); const float r = textured?(s>0?(float)(color.get_shared_channel(0).mean()/255.0f):1.0f):(s>0?(float)(color(0)/255.0f):1.0f), g = textured?(s>1?(float)(color.get_shared_channel(1).mean()/255.0f):r) :(s>1?(float)(color(1)/255.0f):r), b = textured?(s>2?(float)(color.get_shared_channel(2).mean()/255.0f):r) :(s>2?(float)(color(2)/255.0f):r); switch (prim) { case 1: std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break; case 2: case 6: std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break; case 3: case 9: if (invert_faces) std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),r,g,b); else std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; case 4: case 12: if (invert_faces) std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),(unsigned int)primitives(l,2),(unsigned int)primitives(l,3),r,g,b); else std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n", (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),(unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break; } } if (!file) cimg::fclose(nfile); return *this; } //! Save OFF files (GeomView 3D object files) template const CImg& save_off(const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { return save_off(0,filename,primitives,colors,invert_faces); } //! Get a 40x38 color logo of a 'danger' item static CImg get_logo40x38() { static bool first_time = true; static CImg res(40,38,1,3); if (first_time) { const unsigned char *ptrs = cimg::logo40x38; T *ptr1 = res.ptr(0,0,0,0), *ptr2 = res.ptr(0,0,0,1), *ptr3 = res.ptr(0,0,0,2); for (unsigned int off = 0; off structure # # # #------------------------------------------ */ //! Class representing list of images CImg. template struct CImgList { //! Size of the list (number of elements inside) unsigned int size; //! Allocation size of the list unsigned int allocsize; //! Pointer to the first list element CImg *data; //! Define a CImgList::iterator typedef CImg* iterator; //! Define a CImgList::const_iterator typedef const CImg* const_iterator; //! Get value type typedef T value_type; //@} //--------------------------- // //! \name Plugins //@{ //--------------------------- #ifdef cimglist_plugin #include cimglist_plugin #endif //@} //------------------------------------------ // //! \name Constructors - Destructor - Copy //@{ //------------------------------------------ //! Default constructor CImgList(): size(0),allocsize(0),data(0) {} //! Destructor ~CImgList() { if (data) delete[] data; } //! In-place version of the default constructor and default destructor CImgList& assign() { if (data) delete[] data; size = allocsize = 0; data = 0; return *this; } //! Equivalent to assign() (STL-compliant name) CImgList& clear() { return assign(); } //! Copy constructor template CImgList(const CImgList& list): size(0),allocsize(0),data(0) { assign(list); } CImgList(const CImgList& list): size(0),allocsize(0),data(0) { assign(list.size); cimglist_for(*this,l) (*this)[l].assign(list[l],list[l].is_shared); } //! Copy constructor that create a shared object template CImgList(const CImgList& list, const bool shared): size(0),allocsize(0),data(0) { assign(list,shared?1:0); } CImgList(const CImgList& list, const bool shared): size(0),allocsize(0),data(0) { assign(list,shared?1:0); } //! In-place version of the copy constructor template CImgList& assign(const CImgList& list, const int shared=0) { assign(list.size); if (shared>=0) cimglist_for(*this,l) (*this)[l].assign(list[l],shared?true:false); else cimglist_for(*this,l) (*this)[l].assign(list[l],list[l].is_shared); return *this; } //! Construct an image list containing n empty images explicit CImgList(const unsigned int n): size(n) { data = new CImg[allocsize=cimg::nearest_pow2(n)]; } //! In-place version of the previous constructor CImgList& assign(const unsigned int n) { if (n) { if (allocsize(n<<2)) { if (data) delete[] data; data = new CImg[allocsize=cimg::nearest_pow2(n)]; } size = n; } else return assign(); return *this; } //! Construct an image list containing n images with specified size CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int dim=1): size(0),allocsize(0),data(0) { assign(n,width,height,depth,dim); } //! In-place version of the previous constructor CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height=1, const unsigned int depth=1, const unsigned int dim=1) { const unsigned int siz = width*height*depth*dim; if (n && siz) { assign(n); cimglist_for(*this,l) data[l].assign(width,height,depth,dim); } else return assign(); return *this; } //! Construct an image list containing n images with specified size, filled with val CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const T& val): size(0),allocsize(0),data(0) { assign(n,width,height,depth,dim,val); } //! In-place version of the previous constructor CImgList& assign(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const T& val) { assign(n,width,height,depth,dim); cimglist_for(*this,l) data[l].fill(val); return *this; } //! Construct an image list containing n images with specified size and specified pixel values (int version). CImgList(const unsigned int n, const unsigned int width, const unsigned int height, const unsigned int depth, const unsigned int dim, const int val0, const int val1, ...): size(0),allocsize(0),data(0) { #define _CImgList_stdarg(t) \ assign(n,width,height,depth,dim); \ const unsigned int siz = width*height*depth*dim, nsiz = siz*n; \ T *ptrd = data->data; \ va_list ap; \ va_start(ap,val1); \ for (unsigned int l=0, s=0, i=0; i CImgList(const unsigned int n, const CImg& img, const bool shared=false): size(0),allocsize(0),data(0) { assign(n,img,shared); } //! In-place version of the previous constructor template CImgList& assign(const unsigned int n, const CImg& img, const bool shared=false) { assign(n); cimglist_for(*this,l) data[l].assign(img,shared); return *this; } //! Construct an image list from one image template explicit CImgList(const CImg& img, const bool shared=false): size(0),allocsize(0),data(0) { assign(img,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img, const bool shared=false) { return assign(1,img,shared); } //! Construct an image list from two images template CImgList(const CImg& img1, const CImg& img2, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const bool shared=false) { assign(2); data[0].assign(img1,shared); data[1].assign(img2,shared); return *this; } //! Construct an image list from three images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const bool shared=false) { assign(3); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); return *this; } //! Construct an image list from four images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,img4,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const bool shared=false) { assign(4); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); return *this; } //! Construct an image list from five images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,img4,img5,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const bool shared=false) { assign(5); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); return *this; } //! Construct an image list from six images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,img4,img5,img6,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const bool shared=false) { assign(6); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); return *this; } //! Construct an image list from seven images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,img4,img5,img6,img7,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const bool shared=false) { assign(7); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); return *this; } //! Construct an image list from eight images template CImgList(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false): size(0),allocsize(0),data(0) { assign(img1,img2,img3,img4,img5,img6,img7,img8,shared); } //! In-place version of the previous constructor template CImgList& assign(const CImg& img1, const CImg& img2, const CImg& img3, const CImg& img4, const CImg& img5, const CImg& img6, const CImg& img7, const CImg& img8, const bool shared=false) { assign(8); data[0].assign(img1,shared); data[1].assign(img2,shared); data[2].assign(img3,shared); data[3].assign(img4,shared); data[4].assign(img5,shared); data[5].assign(img6,shared); data[6].assign(img7,shared); data[7].assign(img8,shared); return *this; } //! Construct an image list from a filename CImgList(const char *const filename): size(0),allocsize(0),data(0) { assign(filename); } //! In-place version of the previous constructor CImgList& assign(const char *const filename) { return load(filename); } // INNER ROUTINE : Swap fields of two CImgList instances. CImgList& swap(CImgList& list) { cimg::swap(size,list.size); cimg::swap(allocsize,list.allocsize); cimg::swap(data,list.data); return list; } // INNER ROUTINE : Assign a temporary image list to 'list'. #ifndef cimg_use_visualcpp6 CImgList& assign_to(CImgList& list) { return swap(list); } #endif template CImgList& assign_to(CImgList& list) { return list.assign(*this); } //! Return a string describing the type of the image pixels in the list (template parameter \p T). static const char* pixel_type() { return cimg::type::string(); } //! Return \p true if list is empty bool is_empty() const { return (!data || !size); } operator bool() const { return !is_empty(); } //! Return \c true if the list contains an image with indice k bool contains(const int k) const { return data && k<(int)size; } //! Return \c true if the k-th image of the list contains the pixel (x,y,z,v) bool contains(const int k, const int x, const int y=0, const int z=0, const int v=0) const { return contains(k) && data[k].contains(x,y,z,v); } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel, t& l, t& x, t&y, t& z, t& v) const { const T *ptr = &pixel; cimglist_for(*this,i) if (data[i].contains(pixel,x,y,z,v)) { l = (t)i; return true; } return false; } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel, t& l, t& x, t&y, t& z) const { t v; return contains(pixel,l,x,y,z,v); } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel, t& l, t& x, t&y) const { t z,v; return contains(pixel,l,x,y,z,v); } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel, t& l, t& x) const { t y,z,v; return contains(pixel,l,x,y,z,v); } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel, t& l) const { t x,y,z,v; return contains(pixel,l,x,y,z,v); } //! Return \c true if one of the image list contains the pixel. template bool contains(const T& pixel) const { t l,x,y,z,v; return contains(pixel,l,x,y,z,v); } //@} //------------------------------ // //! \name Arithmetics Operators //@{ //------------------------------ //! Assignment operator template CImgList& operator=(const CImgList& list) { return assign(list); } CImgList& operator=(const CImgList& list) { return assign(list); } //! Assignment operator. template CImgList& operator=(const CImg& img) { cimglist_for(*this,l) data[l]=img; return *this; } //! Assignment operator. CImgList& operator=(const T& val) { cimglist_for(*this,l) data[l].fill(val); return *this; } //! Operator+ CImgList operator+() const { return CImgList(*this); } //! Operator+= #ifdef cimg_use_visualcpp6 CImgList& operator+=(const T& val) { #else template CImgList& operator+=(const t& val) { #endif cimglist_for(*this,l) (*this)[l]+=val; return *this; } //! Operator+= template CImgList& operator+=(const CImgList& list) { const unsigned int sizemax = cimg::min(size,list.size); for (unsigned int l=0; l copy(*this); ++*this; return copy; } //! Operator- CImgList operator-() const { CImgList res(size); cimglist_for(res,l) res[l].assign(-data[l]); return res; } //! Operator-=. #ifdef cimg_use_visualcpp6 CImgList& operator-=(const T& val) { #else template CImgList& operator-=(const t& val) { #endif cimglist_for(*this,l) (*this)[l]-=val; return *this; } //! Operator-=. template CImgList& operator-=(const CImgList& list) { const unsigned int sizemax = min(size,list.size); for (unsigned int l=0; l copy(*this); --*this; return copy; } //! Operator*=. #ifdef cimg_use_visualcpp6 CImgList& operator*=(const double val) { #else template CImgList& operator*=(const t& val) { #endif cimglist_for(*this,l) (*this)[l]*=val; return *this; } //! Operator*=. template CImgList& operator*=(const CImgList& list) { const unsigned int N = cimg::min(size,list.size); for (unsigned int l=0; l CImgList& operator/=(const t& val) { #endif cimglist_for(*this,l) (*this)[l]/=val; return *this; } //! Operator/=. template CImgList& operator/=(const CImgList& list) { const unsigned int N = cimg::min(size,list.size); for (unsigned int l=0; l::max() : Instance image list is empty.",pixel_type()); const T *ptrmax = data->data; T max_value = *ptrmax; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); } return *ptrmax; } //! Return a reference to the maximum pixel value of the instance image T& max() { if (is_empty()) throw CImgInstanceException("CImgList<%s>::max() : Instance image list is empty.",pixel_type()); T *ptrmax = data->data; T max_value = *ptrmax; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)>max_value) max_value = *(ptrmax=ptr); } return *ptrmax; } //! Return a reference to the minimum pixel value of the instance image const T& min() const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::min() : Instance image list is empty.",pixel_type()); const T *ptrmin = data->data; T min_value = *ptrmin; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr)::min() : Instance image list is empty.",pixel_type()); T *ptrmin = data->data; T min_value = *ptrmin; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) if ((*ptr) const T& minmax(t& max_val) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.",pixel_type()); const T *ptrmin = data->data; T min_value = *ptrmin, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T& val = *ptr; if (valmax_value) max_value = val; } } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value of the instance image template T& minmax(t& max_val) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::minmax() : Instance image list is empty.",pixel_type()); T *ptrmin = data->data; T min_value = *ptrmin, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T& val = *ptr; if (valmax_value) max_value = val; } } max_val = (t)max_value; return *ptrmin; } //! Return a reference to the minimum pixel value of the instance image template const T& maxmin(t& min_val) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.",pixel_type()); const T *ptrmax = data->data; T min_value = *ptrmax, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T& val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val T& maxmin(t& min_val) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::maxmin() : Instance image list is empty.",pixel_type()); T *ptrmax = data->data; T min_value = *ptrmax, max_value = min_value; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const T& val = *ptr; if (val>max_value) { max_value = val; ptrmax = ptr; } if (val::mean() : Instance image list is empty.",pixel_type()); double val = 0; unsigned int siz = 0; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) val+=(double)*ptr; siz+=img.size(); } return val/siz; } //! Return the variance of the image double variance() { if (is_empty()) throw CImgInstanceException("CImgList<%s>::variance() : Instance image list is empty.",pixel_type()); double res = 0; unsigned int siz = 0; double S = 0, S2 = 0; cimglist_for(*this,l) { const CImg& img = data[l]; cimg_for(img,ptr,T) { const double val = (double)*ptr; S+=val; S2+=val*val; } siz+=img.size(); } res = (S2 - S*S/siz)/siz; return res; } //@} //------------------------- // //! \name List Manipulation //@{ //------------------------- //! Return a reference to the i-th element of the image list. CImg& operator[](const unsigned int pos) { #if cimg_debug>=3 if (pos>=size) { cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images",pixel_type(),pos,size); return *data; } #endif return data[pos]; } const CImg& operator[](const unsigned int pos) const { #if cimg_debug>=3 if (pos>=size) { cimg::warn("CImgList<%s>::operator[] : bad list position %u, in a list of %u images",pixel_type(),pos,size); return *data; } #endif return data[pos]; } //! Equivalent to CImgList::operator[] CImg& operator()(const unsigned int pos) { return (*this)[pos]; } const CImg& operator()(const unsigned int pos) const { return (*this)[pos]; } //! Return a reference to (x,y,z,v) pixel of the pos-th image of the list T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) { return (*this)[pos](x,y,z,v); } const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int v=0) const { return (*this)[pos](x,y,z,v); } //! Equivalent to CImgList::operator[], with boundary checking CImg& at(const unsigned int pos) { if (pos>=size) throw CImgArgumentException("CImgList<%s>::at() : bad list position %u, in a list of %u images", pixel_type(),pos,size); return data[pos]; } const CImg& at(const unsigned int pos) const { if (pos>=size) throw CImgArgumentException("CImgList<%s>::at() : bad list position %u, in a list of %u images", pixel_type(),pos,size); return data[pos]; } //! Returns a reference to last element CImg& back() { return (*this)(size-1); } const CImg& back() const { return (*this)(size-1); } //! Returns a reference to the first element CImg& front() { return *data; } const CImg& front() const { return *data; } //! Returns an iterator to the beginning of the vector. iterator begin() { return data; } const_iterator begin() const { return data; } //! Returns an iterator just past the last element. iterator end() { return data + size; } const_iterator end() const { return data + size; } //! Insert a copy of the image \p img into the current image list, at position \p pos. template CImgList::type> get_insert(const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).insert(img,pos,shared); } //! In-place version of the previous function. template CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { const unsigned int npos = pos==~0U?size:pos; if (npos>size) throw CImgArgumentException("CImgList<%s>::insert() : Cannot insert at position %u into a list with %u elements", pixel_type(),npos,size); if (shared) throw CImgArgumentException("CImgList<%s>::insert(): Cannot insert a shared image CImg<%s> into a CImgList<%s>", pixel_type(),img.pixel_type(),pixel_type()); CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=1)]:0; if (!size || !data) { data = new_data; *data = img; } else { if (new_data) { if (npos) std::memcpy(new_data,data,sizeof(CImg)*npos); if (npos!=size-1) std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); std::memset(data,0,sizeof(CImg)*(size-1)); delete[] data; data = new_data; } else if (npos!=size-1) std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; data[npos] = img; } return *this; } CImgList& insert(const CImg& img, const unsigned int pos, const bool shared) { const unsigned int npos = pos==~0U?size:pos; if (npos>size) throw CImgArgumentException("CImgList<%s>::insert() : Can't insert at position %u into a list with %u elements", pixel_type(),npos,size); CImg *new_data = (++size>allocsize)?new CImg[allocsize?(allocsize<<=1):(allocsize=1)]:0; if (!size || !data) { data = new_data; if (shared && img) { data->width = img.width; data->height = img.height; data->depth = img.depth; data->dim = img.dim; data->is_shared = true; data->data = img.data; } else *data = img; } else { if (new_data) { if (npos) std::memcpy(new_data,data,sizeof(CImg)*npos); if (npos!=size-1) std::memcpy(new_data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); if (shared && img) { new_data[npos].width = img.width; new_data[npos].height = img.height; new_data[npos].depth = img.depth; new_data[npos].dim = img.dim; new_data[npos].is_shared = true; new_data[npos].data = img.data; } else { new_data[npos].width = new_data[npos].height = new_data[npos].depth = new_data[npos].dim = 0; new_data[npos].data = 0; new_data[npos] = img; } std::memset(data,0,sizeof(CImg)*(size-1)); delete[] data; data = new_data; } else { if (npos!=size-1) std::memmove(data+npos+1,data+npos,sizeof(CImg)*(size-1-npos)); if (shared && img) { data[npos].width = img.width; data[npos].height = img.height; data[npos].depth = img.depth; data[npos].dim = img.dim; data[npos].is_shared = true; data[npos].data = img.data; } else { data[npos].width = data[npos].height = data[npos].depth = data[npos].dim = 0; data[npos].data = 0; data[npos] = img; } } } return *this; } // The two functions below are necessary due to Visual C++ 6.0 function overloading bugs, when // default parameters are used in function signatures. template CImgList& insert(const CImg& img, const unsigned int pos) { return insert(img,pos,false); } //! In-place version of the previous function. template CImgList& insert(const CImg& img) { return insert(img,~0U,false); } //! Insert n copies of the image \p img into the current image list, at position \p pos. template CImgList::type> get_insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).insert(n,img,pos,shared); } //! In-place version of the previous function. template CImgList& insert(const unsigned int n, const CImg& img, const unsigned int pos=~0U, const bool shared=false) { const unsigned int npos = pos==~0U?size:pos; insert(img,npos,shared); for (unsigned int i=1; i CImgList::type> get_insert(const CImgList& list, const unsigned int pos=~0U, int shared=0) const { typedef typename cimg::superset::type restype; return CImgList(*this).insert(list,pos,shared); } //! In-place version of the previous function. template CImgList& insert(const CImgList& list, const unsigned int pos=~0U, const int shared=0) { const unsigned int npos = pos==~0U?size:pos; if ((void*)this!=(void*)&list) { if (shared>=0) cimglist_for(list,l) insert(list[l],npos+l,shared?true:false); else cimglist_for(list,l) insert(list[l],npos+l,list[l].is_shared); } else insert(CImgList(list),npos,shared); return *this; } //! Insert n copies of the list \p list at position \p pos of the current list. template CImgList::type> get_insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const int shared=0) const { typedef typename cimg::superset::type restype; return CImgList(*this).insert(n,list,pos,shared); } //! In-place version of the previous function. template CImgList& insert(const unsigned int n, const CImgList& list, const unsigned int pos=~0U, const int shared=0) { const unsigned int npos = pos==~0U?size:pos; for (unsigned int i=0; i=size) cimg::warn("CImgList<%s>::remove() : Cannot remove an image from a list (%p,%u), at position %u.", pixel_type(),data,size,pos); else { data[pos].assign(); if (!(--size)) return assign(); if (size<8 || size>(allocsize>>2)) // Removing item without reallocation. { if (pos!=size) { std::memmove(data+pos,data+pos+1,sizeof(CImg)*(size-pos)); CImg &tmp = data[size]; tmp.width = tmp.height = tmp.depth = tmp.dim = 0; tmp.data = 0; } } else // Removing item with reallocation. { allocsize>>=2; CImg *new_data = new CImg[allocsize]; if (pos) std::memcpy(new_data,data,sizeof(CImg)*pos); if (pos!=size) std::memcpy(new_data+pos,data+pos+1,sizeof(CImg)*(size-pos)); std::memset(data,0,sizeof(CImg)*(size+1)); delete[] data; data = new_data; } } return *this; } //! Remove the last image from the image list. CImgList get_remove() const { return (+*this).remove(); } //! In-place version of the previous function. CImgList& remove() { if (size) return remove(size-1); else cimg::warn("CImgList<%s>::remove() : List is empty",pixel_type()); return *this; } //! Reverse list order CImgList get_reverse() const { return (+*this).reverse(); } //! In-place version of the previous function. CImgList& reverse() { for (unsigned int l=0; li1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l].assign((*this)[i0+l],shared); return res; } //! In-place version of the previous function. CImgList& crop(const unsigned int i0, const unsigned int i1, const bool shared=false) { return get_crop(i0,i1,shared).assign_to(*this); } //! Get sub-images of a sublist CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,v0,x1,y1,z1,v1); return res; } //! In-place version of the previous function. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int v0, const int x1, const int y1, const int z1, const int v1) { return get_crop(i0,i1,x0,y0,z0,v0,x1,y1,z1,v1).assign_to(*this); } //! Get sub-images of a sublist CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,z0,x1,y1,z1); return res; } //! In-place version of the previous function. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int z0, const int x1, const int y1, const int z1) { return get_crop(i0,i1,x0,y0,z0,x1,y1,z1).assign_to(*this); } //! Get sub-images of a sublist CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int x1, const int y1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,y0,x1,y1); return res; } //! In-place version of the previous function. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int y0, const int x1, const int y1) { return get_crop(i0,i1,x0,y0,x1,y1).assign_to(*this); } //! Get sub-images of a sublist CImgList get_crop(const unsigned int i0, const unsigned int i1, const int x0, const int x1) const { if (i0>i1 || i1>=size) throw CImgArgumentException("CImgList<%s>::crop() : Cannot crop a sub-list (%u->%u) from a list (%u,%p)", pixel_type(),i0,i1,size,data); CImgList res(i1-i0+1); cimglist_for(res,l) res[l] = (*this)[i0+l].get_crop(x0,x1); return res; } //! In-place version of the previous function. CImgList& crop(const unsigned int i0, const unsigned int i1, const int x0, const int x1) { return get_crop(i0,i1,x0,x1).assign_to(*this); } //! Insert a copy of the image \p img at the end of the current image list. template CImgList& operator<<(const CImg& img) { return insert(img); } //! Insert a copy of the image list \p list at the end of the current image list. template CImgList& operator<<(const CImgList& list) { return insert(list); } //! Return a copy of the current image list, where the image \p img has been inserted at the end. template CImgList& operator>>(CImg& img) const { typedef typename cimg::superset::type restype; return CImgList(*this).insert(img); } //! Insert a copy of the current image list at the beginning of the image list \p list. template CImgList& operator>>(CImgList& list) const { return list.insert(*this,0); } //! Display an image list into a CImgDisplay const CImgList& operator>>(CImgDisplay& disp) const { return display(disp); } //! Insert image \p img at the end of the list. template CImgList& push_back(const CImg& img) { return insert(img); } //! Insert image \p img at the front of the list. template CImgList& push_front(const CImg& img) { return insert(img,0); } //! Insert list \p list at the end of the current list. template CImgList& push_back(const CImgList& list) { return insert(list); } //! Insert list \p list at the front of the current list. template CImgList& push_front(const CImgList& list) { return insert(list,0); } //! Remove last element of the list; CImgList& pop_back() { return remove(size-1); } //! Remove first element of the list; CImgList& pop_front() { return remove(0); } //! Remove the element pointed by iterator \p iter; CImgList& erase(const iterator iter) { return remove(iter-data); } //@} //---------------------------- // //! \name Fourier Transforms //@{ //---------------------------- //! Compute the Fast Fourier Transform (along the specified axis). CImgList::type> get_FFT(const char axe, const bool inverse=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).FFT(axe,inverse); } //! In-place version of the previous function. CImgList& FFT(const char axe, const bool inverse=false) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty",pixel_type(),size,data); if (!data[0]) throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) is empty", pixel_type(),data[0].width,data[0].height,data[0].depth,data[0].dim,data[0].data); if (size>2) cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images",pixel_type(),size,data); if (size==1) insert(CImg(data[0].width,data[0].height,data[0].depth,data[0].dim,0)); CImg &Ir = data[0], &Ii = data[1]; if (Ir.width!=Ii.width || Ir.height!=Ii.height || Ir.depth!=Ii.depth || Ir.dim!=Ii.dim) throw CImgInstanceException("CImgList<%s>::FFT() : Real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p)" "have different dimensions",pixel_type(), Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data,Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); #ifdef cimg_use_fftw3 fftw_complex *data_in; fftw_plan data_plan; switch (cimg::uncase(axe)) { case 'x': { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width); data_plan = fftw_plan_dft_1d(Ir.width, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); cimg_forYZV(Ir,y,z,k) { T *ptrr = Ir.ptr(0,y,z,k), *ptri = Ii.ptr(0,y,z,k); double *ptrd = (double*)data_in; cimg_forX(Ir,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); } fftw_execute(data_plan); { cimg_forX(Ir,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); } } } } break; case 'y': { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.height); data_plan = fftw_plan_dft_1d(Ir.height, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); const unsigned int off = Ir.width; cimg_forXZV(Ir,x,z,k) { T *ptrr = Ir.ptr(x,0,z,k), *ptri = Ii.ptr(x,0,z,k); double *ptrd = (double*)data_in; cimg_forY(Ir,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); { cimg_forY(Ir,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } } } } break; case 'z': { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.depth); data_plan = fftw_plan_dft_1d(Ir.depth, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); const unsigned int off = Ir.width*Ir.height; cimg_forXYV(Ir,x,y,k) { T *ptrr = Ir.ptr(x,y,0,k), *ptri = Ii.ptr(x,y,0,k); double *ptrd = (double*)data_in; cimg_forZ(Ir,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); { cimg_forZ(Ir,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } } } } break; case 'v': { data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.dim); data_plan = fftw_plan_dft_1d(Ir.dim, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); const unsigned int off = Ir.width*Ir.height*Ir.depth; cimg_forXYZ(Ir,x,y,z) { T *ptrr = Ir.ptr(x,y,z,0), *ptri = Ii.ptr(x,y,z,0); double *ptrd = (double*)data_in; cimg_forV(Ir,k) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; } fftw_execute(data_plan); { cimg_forV(Ir,k) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); } } } } break; } fftw_destroy_plan(data_plan); fftw_free(data_in); #else switch (cimg::uncase(axe)) { case 'x': // Fourier along X { const unsigned int N = Ir.width, N2 = (N>>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image along 'x' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forYZV(Ir,y,z,v) { cimg::swap(Ir(i,y,z,v),Ir(j,y,z,v)); cimg::swap(Ii(i,y,z,v),Ii(j,y,z,v)); if (j=m; j-=m, m=n, n>>=1); } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'y' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forXZV(Ir,x,z,v) { cimg::swap(Ir(x,i,z,v),Ir(x,j,z,v)); cimg::swap(Ii(x,i,z,v),Ii(x,j,z,v)); if (j=m; j-=m, m=n, n>>=1); } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i>1); if (((N-1)&N) && N!=1) throw CImgInstanceException("CImgList<%s>::FFT() : Dimension of instance image(s) along 'z' is %d != 2^N", pixel_type(),N); for (unsigned int i=0, j=0; ii) cimg_forXYV(Ir,x,y,v) { cimg::swap(Ir(x,y,i,v),Ir(x,y,j,v)); cimg::swap(Ii(x,y,i,v),Ii(x,y,j,v)); if (j=m; j-=m, m=n, n>>=1); } for (unsigned int delta=2; delta<=N; delta<<=1) { const unsigned int delta2 = (delta>>1); for (unsigned int i=0; i::FFT() : unknown axe '%c', must be 'x','y' or 'z'"); } #endif return *this; } //! Compute the Fast Fourier Transform of a complex image. CImgList::type> get_FFT(const bool inverse=false) const { typedef typename cimg::superset::type restype; return CImgList(*this).FFT(inverse); } //! In-place version of the previous function. CImgList& FFT(const bool inverse=false) { if (is_empty()) throw CImgInstanceException("CImgList<%s>::FFT() : Instance list (%u,%p) is empty",pixel_type(),size,data); if (size>2) cimg::warn("CImgList<%s>::FFT() : Instance list (%u,%p) have more than 2 images",pixel_type(),size,data); if (size==1) insert(CImg(data->width,data->height,data->depth,data->dim,0)); CImg &Ir = data[0], &Ii = data[1]; if (Ii.width!=Ir.width || Ii.height!=Ir.height || Ii.depth!=Ir.depth || Ii.dim!=Ir.dim) throw CImgInstanceException("CImgList<%s>::FFT() : Real (%u,%u,%u,%u,%p) and Imaginary (%u,%u,%u,%u,%p) parts " "of the instance image have different dimensions", pixel_type(),Ir.width,Ir.height,Ir.depth,Ir.dim,Ir.data, Ii.width,Ii.height,Ii.depth,Ii.dim,Ii.data); #ifdef cimg_use_fftw3 fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * Ir.width*Ir.height*Ir.depth); fftw_plan data_plan; const unsigned int w = Ir.width, wh = w*Ir.height, whd = wh*Ir.depth; data_plan = fftw_plan_dft_3d(Ir.width, Ir.height, Ir.depth, data_in, data_in, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE); cimg_forV(Ir,k) { T *ptrr = Ir.ptr(0,0,0,k), *ptri = Ii.ptr(0,0,0,k); double *ptrd = (double*)data_in; cimg_forX(Ir,x) { cimg_forY(Ir,y) { cimg_forZ(Ir,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=wh; ptri+=wh; } ptrr-=whd-w; ptri-=whd-w; } ptrr-=wh-1; ptri-=wh-1; } fftw_execute(data_plan); ptrd = (double*)data_in; ptrr = Ir.ptr(0,0,0,k); ptri = Ii.ptr(0,0,0,k); { cimg_forX(Ir,x) { cimg_forY(Ir,y) { cimg_forZ(Ir,z) { *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++); ptrr+=wh; ptri+=wh; } ptrr-=whd-w; ptri-=whd-w; } ptrr-=wh-1; ptri-=wh-1; } } } fftw_destroy_plan(data_plan); fftw_free(data_in); #else if (Ir.depth>1) FFT('z',inverse); if (Ir.height>1) FFT('y',inverse); if (Ir.width>1) FFT('x',inverse); #endif return *this; } //@} //---------------------------------- // //! \name Input-Output and Display //@{ //---------------------------------- //! Print informations about the list on the standard output. const CImgList& print(const char* title=0, const int print_flag=1) const { if (print_flag>=0) { char tmp[1024]; std::fprintf(stderr,"%-8s(this=%p) : { size=%u, data=%p }\n",title?title:"CImgList", (void*)this,size,(void*)data); switch (print_flag) { case 1: { cimglist_for(*this,l) if (l<4 || l>=size-4) { std::sprintf(tmp,"%s[%d]",title?title:"CImgList",l); data[l].print(tmp,print_flag); } else { if (l==4) std::fprintf(stderr,"...\n"); } } break; default: { cimglist_for(*this,l) { std::sprintf(tmp,"%s[%d]",title?title:"CImgList",l); data[l].print(tmp,print_flag); } } break; } } return *this; } //! Display informations about the list on the standard output. const CImgList& print(const int print_flag) const { return print(0,print_flag); } //! Load an image list from a file. static CImgList get_load(const char *const filename) { return CImgList().load(filename); } //! In-place version of the previous function. CImgList& load(const char *const filename) { const char *ext = cimg::filename_split(filename); #ifdef cimglist_load_plugin cimglist_load_plugin(filename); #endif if (!cimg::strncasecmp(ext,"cimg",4) || !ext[0]) return load_cimg(filename); if (!cimg::strncasecmp(ext,"rec",3) || !cimg::strncasecmp(ext,"par",3)) return load_parrec(filename); assign(1); data->load(filename); return *this; } //! Load an image list from a .cimg file. static CImgList get_load_cimg(std::FILE *const file, const char *const filename=0) { return CImgList().load_cimg(file,filename); } //! In-place version of the previous function. CImgList& load_cimg(std::FILE *const file, const char *const filename=0) { #define cimg_load_cimg_case(Ts,Tss) \ if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l=0; l::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ if (W*H*D*V>0) { \ CImg raw; \ CImg &img = data[l]; \ img.assign(W,H,D,V); \ T *ptrd = img.data; \ for (int toread = (int)img.size(); toread>0; ) { \ raw.assign(cimg::min(toread,cimg_iobuffer)); \ cimg::fread(raw.data,raw.width,nfile); \ if (endian!=cimg::endian()) cimg::endian_swap(raw.data,raw.width); \ toread-=raw.width; \ const Tss *ptrs = raw.data; \ for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ } \ } \ } \ loaded = true; \ } typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; const int cimg_iobuffer = 12*1024*1024; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool loaded = false, endian = cimg::endian(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N=0, W, H, D, V; int i; j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; assign(N); cimg_load_cimg_case("bool",bool); cimg_load_cimg_case("unsigned_char",uchar); cimg_load_cimg_case("uchar",uchar); cimg_load_cimg_case("char",char); cimg_load_cimg_case("unsigned_short",ushort); cimg_load_cimg_case("ushort",ushort); cimg_load_cimg_case("short",short); cimg_load_cimg_case("unsigned_int",uint); cimg_load_cimg_case("uint",uint); cimg_load_cimg_case("int",int); cimg_load_cimg_case("unsigned_long",ulong); cimg_load_cimg_case("ulong",ulong); cimg_load_cimg_case("long",long); cimg_load_cimg_case("float",float); cimg_load_cimg_case("double",double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image list from a .cimg file. static CImgList get_load_cimg(const char *const filename) { return CImgList().load_cimg(0,filename); } //! In-place version of the previous function. CImgList& load_cimg(const char *const filename) { return load_cimg(0,filename); } //! Load a sub-image list from a .cimg file. static CImgList get_load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return CImgList().load_cimg(file,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } //! In-place version of the previous function. CImgList& load_cimg(std::FILE *const file, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return load_cimg(file,0,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } //! Load a sub-image list from a .cimg file. static CImgList get_load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return CImgList().load_cimg(filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } //! In-place version of the previous function. CImgList& load_cimg(const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { return load_cimg(0,filename,n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); } //! In-place version of the previous function. CImgList& load_cimg(std::FILE *const file, const char *const filename, const unsigned int n0, const unsigned int n1, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0, const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int v1) { #define cimg_load_cimg_case2(Ts,Tss) \ if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l=0; l<=nn1; ++l) { \ j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j]='\0'; \ W = H = D = V = 0; \ if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&V)!=4) \ throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ if (W*H*D*V>0) { \ if (l=W || y0>=H || z0>=D || v0>=D) std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ else { \ const unsigned int \ nx1 = x1>=W?W-1:x1, \ ny1 = y1>=H?H-1:y1, \ nz1 = z1>=D?D-1:z1, \ nv1 = v1>=V?V-1:v1; \ CImg raw(1+nx1-x0); \ CImg &img = data[l-n0]; \ img.assign(1+nx1-x0,1+ny1-y0,1+nz1-z0,1+nv1-v0); \ T *ptrd = img.data; \ const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ for (unsigned int v=1+nv1-v0; v; --v) { \ const unsigned int skipzb = z0*W*H*sizeof(Tss); \ if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \ for (unsigned int z=1+nz1-z0; z; --z) { \ const unsigned int skipyb = y0*W*sizeof(Tss); \ if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \ for (unsigned int y=1+ny1-y0; y; --y) { \ const unsigned int skipxb = x0*sizeof(Tss); \ if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \ cimg::fread(raw.data,raw.width,nfile); \ if (endian!=cimg::endian()) cimg::endian_swap(raw.data,raw.width); \ const Tss *ptrs = raw.data; \ for (unsigned int off = raw.width; off; --off) *(ptrd++) = (T)*(ptrs++); \ const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \ } \ const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \ } \ const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \ } \ const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ loaded = true; \ } typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; if (n1::load_cimg() : File '%s', Bad sub-region coordinates [%u->%u] " "(%u,%u,%u,%u)->(%u,%u,%u,%u).",pixel_type(),filename?filename:"(FILE*)", n0,n1,x0,y0,z0,v0,x1,y1,z1,v1); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool loaded = false, endian = cimg::endian(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N, W, H, D, V; int i; j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; const unsigned int nn1 = n1>=N?N-1:n1; assign(1+nn1-n0); cimg_load_cimg_case2("bool",bool); cimg_load_cimg_case2("unsigned_char",uchar); cimg_load_cimg_case2("uchar",uchar); cimg_load_cimg_case2("char",char); cimg_load_cimg_case2("unsigned_short",ushort); cimg_load_cimg_case2("ushort",ushort); cimg_load_cimg_case2("short",short); cimg_load_cimg_case2("unsigned_int",uint); cimg_load_cimg_case2("uint",uint); cimg_load_cimg_case2("int",int); cimg_load_cimg_case2("unsigned_long",ulong); cimg_load_cimg_case2("ulong",ulong); cimg_load_cimg_case2("long",long); cimg_load_cimg_case2("float",float); cimg_load_cimg_case2("double",double); if (!loaded) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_cimg() : File '%s', cannot read images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Load an image list from a PAR/REC (Philips) file. static CImgList get_load_parrec(const char *const filename) { return CImgList().load_parrec(filename); } //! In-place version of the previous function. CImgList& load_parrec(const char *const filename) { char body[1024], filenamepar[1024], filenamerec[1024]; const char *ext = cimg::filename_split(filename,body); if (!cimg::strncmp(ext,"par",3)) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.rec",body); } if (!cimg::strncmp(ext,"PAR",3)) { std::strcpy(filenamepar,filename); std::sprintf(filenamerec,"%s.REC",body); } if (!cimg::strncmp(ext,"rec",3)) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.par",body); } if (!cimg::strncmp(ext,"REC",3)) { std::strcpy(filenamerec,filename); std::sprintf(filenamepar,"%s.PAR",body); } std::FILE *file = cimg::fopen(filenamepar,"r"); // Parse header file CImgList st_slices; CImgList st_global; int err; char line[256] = { 0 }; do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (line[0]=='#' || line[0]=='.')); do { unsigned int sn,sizex,sizey,pixsize; float rs,ri,ss; err=std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&sizex,&sizey,&ri,&rs,&ss); if (err==7) { st_slices.insert(CImg::vector((float)sn,(float)pixsize,(float)sizex,(float)sizey,ri,rs,ss,0)); unsigned int i; for (i=0; i::vector(sizex,sizey,sn)); else { CImg &vec = st_global[i]; if (sizex>vec[0]) vec[0] = sizex; if (sizey>vec[1]) vec[1] = sizey; vec[2] = sn; } st_slices[st_slices.size-1][7] = (float)i; } } while (err==7); // Read data std::FILE *file2 = cimg::fopen(filenamerec,"rb"); { cimglist_for(st_global,l) { const CImg& vec = st_global[l]; insert(CImg(vec[0],vec[1],vec[2])); } } cimglist_for(st_slices,l) { const CImg& vec = st_slices[l]; const unsigned int sn = (unsigned int)vec[0]-1, pixsize = (unsigned int)vec[1], sizex = (unsigned int)vec[2], sizey = (unsigned int)vec[3], imn = (unsigned int)vec[7]; const float ri = vec[4], rs = vec[5], ss = vec[6]; switch (pixsize) { case 8: { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 16: { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; case 32: { CImg buf(sizex,sizey); cimg::fread(buf.data,sizex*sizey,file2); if (cimg::endian()) cimg::endian_swap(buf.data,sizex*sizey); CImg& img = (*this)[imn]; cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss)); } break; default: cimg::fclose(file); cimg::fclose(file2); throw CImgIOException("CImg<%s>::load_parrec() : File '%s', cannot handle image with pixsize = %d bits.", pixel_type(),filename,pixsize); break; } } cimg::fclose(file); cimg::fclose(file2); if (!size) throw CImgIOException("CImg<%s>::load_parrec() : File '%s' does not appear to be a valid PAR-REC file.", pixel_type(),filename); return *this; } //! Load an image sequence from a YUV file. static CImgList get_load_yuv(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb=false) { return CImgList().load_yuv(file,filename,sizex,sizey,first_frame,last_frame,yuv2rgb); } //! In-place version of the previous function. CImgList& load_yuv(std::FILE *const file, const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb=false) { if (sizex%2 || sizey%2) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', image dimensions along X and Y must be " "even numbers (given are %ux%u)\n",pixel_type(),filename?filename:"(unknown)",sizex,sizey); if (!sizex || !sizey) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', given image sequence size (%u,%u) is invalid", pixel_type(),filename?filename:"(unknown)",sizex,sizey); if (last_frame>0 && first_frame>(unsigned int)last_frame) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', given first frame %u is posterior to last frame %d.", pixel_type(),filename?filename:"(unknown)",first_frame,last_frame); if (!sizex || !sizey) throw CImgArgumentException("CImgList<%s>::load_yuv() : File '%s', given frame size (%u,%u) is invalid.", pixel_type(),filename?filename:"(unknown)",sizex,sizey); CImg tmp(sizex,sizey,1,3), UV(sizex/2,sizey/2,1,2); std::FILE *const nfile = file?file:cimg::fopen(filename,"rb"); bool stopflag = false; int err; if (first_frame) { err = std::fseek(nfile,first_frame*(sizex*sizey + sizex*sizey/2),SEEK_CUR); if (err) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::load_yuv() : File '%s' doesn't contain frame number %u " "(out of range error).",pixel_type(),filename?filename:"(FILE*)",first_frame); } } unsigned int frame; for (frame = first_frame; !stopflag && (last_frame<0 || frame<=(unsigned int)last_frame); ++frame) { tmp.fill(0); // TRY to read the luminance, don't replace by cimg::fread ! err = (int)std::fread((void*)(tmp.data),1,(size_t)(tmp.width*tmp.height),nfile); if (err!=(int)(tmp.width*tmp.height)) { stopflag = true; if (err>0) cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," " or given image dimensions (%u,%u) are incorrect.", pixel_type(),filename?filename:"(unknown)",sizex,sizey); } else { UV.fill(0); // TRY to read the luminance, don't replace by cimg::fread ! err = (int)std::fread((void*)(UV.data),1,(size_t)(UV.size()),nfile); if (err!=(int)(UV.size())) { stopflag = true; if (err>0) cimg::warn("CImgList<%s>::load_yuv() : File '%s' contains incomplete data," " or given image dimensions (%u,%u) are incorrect.", pixel_type(),filename?filename:"(unknown)",sizex,sizey); } else { cimg_forXY(UV,x,y) { const int x2=2*x, y2=2*y; tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0); tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1); } if (yuv2rgb) tmp.YCbCrtoRGB(); insert(tmp); } } } if (stopflag && last_frame>=0 && frame!=(unsigned int)last_frame) cimg::warn("CImgList<%s>::load_yuv() : File '%s', frame %d not reached since only %u frames were found in the file.", pixel_type(),filename?filename:"(unknown)",last_frame,frame-1,filename); if (!file) cimg::fclose(nfile); return *this; } //! Load an image sequence from a YUV file. static CImgList get_load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey=1, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb=false) { return CImgList().load_yuv(0,filename,sizex,sizey,first_frame,last_frame,yuv2rgb); } //! In-place version of the previous function. CImgList& load_yuv(const char *const filename, const unsigned int sizex, const unsigned int sizey, const unsigned int first_frame=0, const int last_frame=-1, const bool yuv2rgb=false) { return load_yuv(0,filename,sizex,sizey,first_frame,last_frame,yuv2rgb); } //! Load a 3D object from a .OFF file (GeomView 3D object files). template static CImgList get_load_off(std::FILE *const file, const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImgList().load_off(file,filename,primitives,colors,invert_faces); } //! In-place version of the previous function. template CImgList& load_off(std::FILE *const file, const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return assign(CImg::get_load_off(file,filename,primitives,colors,invert_faces).get_split('x')); } //! Load a 3D object from a .OFF file (GeomView 3D object files). template static CImgList get_load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return CImgList().load_off(0,filename,primitives,colors,invert_faces); } //! In-place version of the previous function. template CImgList& load_off(const char *const filename, CImgList& primitives, CImgList& colors, const bool invert_faces=false) { return load_off(0,filename,primitives,colors,invert_faces); } //! Save an image list into a file. /** Depending on the extension of the given filename, a file format is chosen for the output file. **/ const CImgList& save(const char *const filename, const int number=-1) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save() : Instance list (%u,%p) is empty (file '%s').", pixel_type(),size,data,filename); if (!filename) throw CImgArgumentException("CImg<%s>::save() : Instance list (%u,%p), specified filename is (null).", pixel_type(),size,data); const char *ext = cimg::filename_split(filename); char nfilename[1024]; const char *const fn = (number>=0)?cimg::filename_number(filename,number,6,nfilename):filename; #ifdef cimglist_save_plugin cimglist_save_plugin(fn); #endif if (!cimg::strncasecmp(ext,"cimg",4) || !ext[0]) return save_cimg(fn); if (!cimg::strncasecmp(ext,"yuv",3)) return save_yuv(fn,true); if (size==1) data[0].save(fn,-1); else cimglist_for(*this,l) data[l].save(fn,l); return *this; } //! Save an image sequence into a YUV file const CImgList& save_yuv(std::FILE *const file, const char *const filename=0, const bool rgb2yuv=true) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_yuv() : Instance list (%u,%p) is empty (file '%s').", pixel_type(),size,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_yuv() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); if ((*this)[0].dimx()%2 || (*this)[0].dimy()%2) throw CImgInstanceException("CImgList<%s>::save_yuv() : Image dimensions must be even numbers (current are %ux%u, file '%s').", pixel_type(),(*this)[0].dimx(),(*this)[0].dimy(),filename?filename:"(unknown)"); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); cimglist_for(*this,l) { CImg YCbCr((*this)[l]); if (rgb2yuv) YCbCr.RGBtoYCbCr(); cimg::fwrite(YCbCr.data,YCbCr.width*YCbCr.height,nfile); cimg::fwrite(YCbCr.get_resize(YCbCr.width/2, YCbCr.height/2,1,3,3).ptr(0,0,0,1), YCbCr.width*YCbCr.height/2,nfile); } if (!file) cimg::fclose(nfile); return *this; } //! Save an image sequence into a YUV file const CImgList& save_yuv(const char *const filename=0, const bool rgb2yuv=true) const { return save_yuv(0,filename,rgb2yuv); } //! Save an image list into a .cimg file. /** A CImg RAW file is a simple uncompressed binary file that may be used to save list of CImg images. \param filename : name of the output file. \return A reference to the current CImgList instance is returned. **/ const CImgList& save_cimg(std::FILE *const file, const char *const filename=0) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_cimg() : Instance list (%u,%p) is empty (file '%s').", pixel_type(),size,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const char *const ptype = pixel_type(), *const etype = cimg::endian()?"big":"little"; if (!cimg::strncmp(ptype,"unsigned",8)) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",size,ptype+9,etype); else std::fprintf(nfile,"%u %s %s_endian\n",size,ptype,etype); cimglist_for(*this,l) { const CImg& img = data[l]; std::fprintf(nfile,"%u %u %u %u\n",img.width,img.height,img.depth,img.dim); if (img.data) { if (cimg::endian()) { CImg tmp(img); cimg::endian_swap(tmp.data,tmp.size()); cimg::fwrite(tmp.data,img.width*img.height*img.depth*img.dim,nfile); } else cimg::fwrite(img.data,img.width*img.height*img.depth*img.dim,nfile); } } if (!file) cimg::fclose(nfile); return *this; } //! Save an image list into a CImg file (RAW binary file + simple header) const CImgList& save_cimg(const char *const filename) const { return save_cimg(0,filename); } // Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& save_cimg(std::FILE *const file, const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { #define cimg_save_cimg_case(Ts,Tss) \ if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \ for (unsigned int l=0; l::save_cimg() : File '%s', Image %u has an invalid size (%u,%u,%u,%u)\n", \ pixel_type(), filename?filename:("(FILE*)"), W, H, D, V); \ if (W*H*D*V>0) { \ if (l=W || y0>=H || z0>=D || v0>=D) std::fseek(nfile,W*H*D*V*sizeof(Tss),SEEK_CUR); \ else { \ const CImg& img = (*this)[l-n0]; \ const T *ptrs = img.data; \ const unsigned int \ x1 = x0 + img.width - 1, \ y1 = y0 + img.height - 1, \ z1 = z0 + img.depth - 1, \ v1 = v0 + img.dim - 1, \ nx1 = x1>=W?W-1:x1, \ ny1 = y1>=H?H-1:y1, \ nz1 = z1>=D?D-1:z1, \ nv1 = v1>=V?V-1:v1; \ CImg raw(1+nx1-x0); \ const unsigned int skipvb = v0*W*H*D*sizeof(Tss); \ if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \ for (unsigned int v=1+nv1-v0; v; --v) { \ const unsigned int skipzb = z0*W*H*sizeof(Tss); \ if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \ for (unsigned int z=1+nz1-z0; z; --z) { \ const unsigned int skipyb = y0*W*sizeof(Tss); \ if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \ for (unsigned int y=1+ny1-y0; y; --y) { \ const unsigned int skipxb = x0*sizeof(Tss); \ if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \ raw.assign(ptrs, raw.width); \ ptrs+=img.width; \ if (endian) cimg::endian_swap(raw.data,raw.width); \ cimg::fwrite(raw.data,raw.width,nfile); \ const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \ if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \ } \ const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \ if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \ } \ const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \ if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \ } \ const unsigned int skipve = (V-1-nv1)*W*H*D*sizeof(Tss); \ if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \ } \ } \ } \ saved = true; \ } if (is_empty()) throw CImgInstanceException("CImgList<%s>::save_cimg() : Instance list (%u,%p) is empty (file '%s').", pixel_type(),size,data,filename?filename:"(unknown)"); if (!file && !filename) throw CImgArgumentException("CImg<%s>::save_cimg() : Instance list (%u,%p), specified file is (null).", pixel_type(),size,data); typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+"); bool saved = false, endian = cimg::endian(); char tmp[256], str_pixeltype[256], str_endian[256]; unsigned int j, err, N, W, H, D, V; int i; j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = '\0'; err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian); if (err<2) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', Unknow CImg RAW header.", pixel_type(),filename?filename:"(FILE*)"); } if (!cimg::strncasecmp("little",str_endian,6)) endian = false; else if (!cimg::strncasecmp("big",str_endian,3)) endian = true; const unsigned int lmax = cimg::min(N,n0+size); cimg_save_cimg_case("bool",bool); cimg_save_cimg_case("unsigned_char",uchar); cimg_save_cimg_case("uchar",uchar); cimg_save_cimg_case("char",char); cimg_save_cimg_case("unsigned_short",ushort); cimg_save_cimg_case("ushort",ushort); cimg_save_cimg_case("short",short); cimg_save_cimg_case("unsigned_int",uint); cimg_save_cimg_case("uint",uint); cimg_save_cimg_case("int",int); cimg_save_cimg_case("unsigned_long",ulong); cimg_save_cimg_case("ulong",ulong); cimg_save_cimg_case("long",long); cimg_save_cimg_case("float",float); cimg_save_cimg_case("double",double); if (!saved) { if (!file) cimg::fclose(nfile); throw CImgIOException("CImgList<%s>::save_cimg() : File '%s', cannot save images of pixels coded as '%s'.", pixel_type(),filename?filename:"(FILE*)",str_pixeltype); } if (!file) cimg::fclose(nfile); return *this; } //! Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& save_cimg(std::FILE *const file, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { return save_cimg(file,0,n0,x0,y0,z0,v0); } //! Insert the instance image into into an existing .cimg file, at specified coordinates. const CImgList& save_cimg(const char *const filename, const unsigned int n0, const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int v0) const { return save_cimg(0,filename,n0,x0,y0,z0,v0); } // Create an empty .cimg file with specified dimensions (inner routine) static void save_empty_cimg(std::FILE *const file, const char *const filename, const unsigned int nb, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { std::FILE *const nfile = file?file:cimg::fopen(filename,"wb"); const unsigned int siz = dx*dy*dz*dv*sizeof(T); std::fprintf(nfile,"%u %s\n",nb,pixel_type()); for (unsigned int i=nb; i; --i) { std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dv); for (unsigned int off=siz; off; --off) std::fputc(0,nfile); } if (!file) cimg::fclose(nfile); } //! Create an empty .cimg file with specified dimensions. static void save_empty_cimg(std::FILE *const file, const unsigned int nb, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { return save_empty_cimg(file,0,nb,dx,dy,dz,dv); } //! Create an empty .cimg file with specified dimensions. static void save_empty_cimg(const char *const filename, const unsigned int nb, const unsigned int dx, const unsigned int dy=1, const unsigned int dz=1, const unsigned int dv=1) { return save_empty_cimg(0,filename,nb,dx,dy,dz,dv); } //! Save an image list into a OFF file. template const CImgList& save_off(std::FILE *const file, const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { get_append('x','y').save_off(file,filename,primitives,colors,invert_faces); return *this; } //! Save an image list into a OFF file. template const CImgList& save_off(const char *const filename, const CImgList& primitives, const CImgList& colors, const bool invert_faces=false) const { return save_off(0,filename,primitives,colors,invert_faces); } // Return a list where each image has been split along the specified axis CImgList get_split(const char axe='x') const { CImgList res; cimglist_for(*this,l) res.insert(data[l].get_split(axe)); return res; } //! In-place version of the previous function. CImgList& split(const char axe='x') { return get_split(axe).assign_to(*this); } //! Return a single image which is the concatenation of all images of the current CImgList instance. /** \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \return A CImg image corresponding to the concatenation is returned. **/ CImg get_append(const char axe='x', const char align='c') const { if (is_empty()) return CImg(); if (size==1) return (*this)[0]; unsigned int dx = 0, dy = 0, dz = 0, dv = 0, pos = 0; CImg res; switch(cimg::uncase(axe)) { case 'x': { switch (cimg::uncase(align)) { case 'x': { dy = dz = dv = 1; cimglist_for(*this,l) dx+=(*this)[l].size(); } break; case 'y': { dx = size; dz = dv = 1; cimglist_for(*this,l) dy = cimg::max(dy,(unsigned int)(*this)[l].size()); } break; case 'z': { dx = size; dy = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'v': { dx = size; dy = dz = 1; cimglist_for(*this,l) dv = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; default: cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx += img.width; dy = cimg::max(dy,img.height); dz = cimg::max(dz,img.depth); dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x': { cimglist_for(*this,l) { res.draw_image(CImg((*this)[l],true).unroll('x'),pos,0,0,0); pos+=(*this)[l].size(); } } break; case 'y': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('y'),pos++,0,0,0); } break; case 'z': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('z'),pos++,0,0,0); } break; case 'v': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('v'),pos++,0,0,0); } break; case 'p': { cimglist_for(*this,l) { res.draw_image((*this)[l],pos,0,0,0); pos+=(*this)[l].width; } } break; case 'n': { cimglist_for(*this,l) { res.draw_image((*this)[l],pos,dy-(*this)[l].height,dz-(*this)[l].depth,dv-(*this)[l].dim); pos+=(*this)[l].width; } } break; default : { cimglist_for(*this,l) { res.draw_image((*this)[l],pos,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2); pos+=(*this)[l].width; } } break; } } break; case 'y': { switch (cimg::uncase(align)) { case 'x': { dy = size; dz = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y': { dx = dz = dv = 1; cimglist_for(*this,l) dy+=(*this)[l].size(); } break; case 'z': { dy = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'v': { dy = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; default: cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy += img.height; dz = cimg::max(dz,img.depth); dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('x'),0,pos++,0,0); } break; case 'y': { cimglist_for(*this,l) { res.draw_image(CImg((*this)[l],true).unroll('y'),0,pos,0,0); pos+=(*this)[l].size(); } } break; case 'z': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('z'),0,pos++,0,0); } break; case 'v': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('v'),0,pos++,0,0); } break; case 'p': { cimglist_for(*this,l) { res.draw_image((*this)[l],0,pos,0,0); pos+=(*this)[l].height; } } break; case 'n': { cimglist_for(*this,l) { res.draw_image((*this)[l],dx-(*this)[l].width,pos,dz-(*this)[l].depth,dv-(*this)[l].dim); pos+=(*this)[l].height; } } break; default : { cimglist_for(*this,l) { res.draw_image((*this)[l],(dx-(*this)[l].width)/2,pos,(dz-(*this)[l].depth)/2,(dv-(*this)[l].dim)/2); pos+=(*this)[l].height; } } break; } } break; case 'z': { switch (cimg::uncase(align)) { case 'x': { dz = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y': { dz = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'z': { dx = dy = dv = 1; cimglist_for(*this,l) dz+=(*this)[l].size(); } break; case 'v': { dz = size; dx = dz = 1; cimglist_for(*this,l) dv = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy = cimg::max(dy,img.height); dz += img.depth; dv = cimg::max(dv,img.dim); } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('x'),0,0,pos++,0); } break; case 'y': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('y'),0,0,pos++,0); } break; case 'z': { cimglist_for(*this,l) { res.draw_image(CImg((*this)[l],true).unroll('z'),0,0,pos,0); pos+=(*this)[l].size(); } } break; case 'v': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('v'),0,0,pos++,0); } break; case 'p': { cimglist_for(*this,l) { res.draw_image((*this)[l],0,0,pos,0); pos+=(*this)[l].depth; } } break; case 'n': { cimglist_for(*this,l) { res.draw_image((*this)[l],dx-(*this)[l].width,dy-(*this)[l].height,pos,dv-(*this)[l].dim); pos+=(*this)[l].depth; } } break; case 'c': { cimglist_for(*this,l) { res.draw_image((*this)[l],(dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,pos,(dv-(*this)[l].dim)/2); pos+=(*this)[l].depth; } } break; } } break; case 'v': { switch (cimg::uncase(align)) { case 'x': { dv = size; dy = dv = 1; cimglist_for(*this,l) dx = cimg::max(dx,(unsigned int)(*this)[l].size()); } break; case 'y': { dv = size; dx = dv = 1; cimglist_for(*this,l) dy = cimg::max(dz,(unsigned int)(*this)[l].size()); } break; case 'z': { dv = size; dx = dv = 1; cimglist_for(*this,l) dz = cimg::max(dv,(unsigned int)(*this)[l].size()); } break; case 'v': { dx = dy = dz = 1; cimglist_for(*this,l) dv+=(*this)[l].size(); } break; default : cimglist_for(*this,l) { const CImg& img = (*this)[l]; dx = cimg::max(dx,img.width); dy = cimg::max(dy,img.height); dz = cimg::max(dz,img.depth); dv += img.dim; } } res.assign(dx,dy,dz,dv,0); switch (cimg::uncase(align)) { case 'x': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('x'),0,0,0,pos++); } break; case 'y': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('y'),0,0,0,pos++); } break; case 'z': { cimglist_for(*this,l) res.draw_image(CImg((*this)[l],true).unroll('v'),0,0,0,pos++); } break; case 'v': { cimglist_for(*this,l) { res.draw_image(CImg((*this)[l],true).unroll('z'),0,0,0,pos); pos+=(*this)[l].size(); } } break; case 'p': { cimglist_for(*this,l) { res.draw_image((*this)[l],0,0,0,pos); pos+=(*this)[l].dim; } } break; case 'n': { cimglist_for(*this,l) { res.draw_image((*this)[l],dx-(*this)[l].width,dy-(*this)[l].height,dz-(*this)[l].depth,pos); pos+=(*this)[l].dim; } } break; case 'c': { cimglist_for(*this,l) { res.draw_image((*this)[l],(dx-(*this)[l].width)/2,(dy-(*this)[l].height)/2,(dz-(*this)[l].depth)/2,pos); pos+=(*this)[l].dim; } } break; } } break; default: throw CImgArgumentException("CImgList<%s>::get_append() : unknow axe '%c', must be 'x','y','z' or 'v'",pixel_type(),axe); } return res; } // Create an auto-cropped font (along the X axis) from a input font \p font. CImgList get_crop_font() const { CImgList res; cimglist_for(*this,l) { const CImg& letter = (*this)[l]; int xmin = letter.width, xmax = 0; cimg_forXY(letter,x,y) if (letter(x,y)) { if (xxmax) xmax=x; } if (xmin>xmax) res.insert(CImg(letter.width,letter.height,1,letter.dim,0)); else res.insert(letter.get_crop(xmin,0,xmax,letter.height-1)); } res[' '].resize(res['f'].width); res[' '+256].resize(res['f'].width); return res; } //! In-place version of the previous function. CImgList& crop_font() { return get_crop_font().assign_to(*this); } // Get a font as an image list. static CImgList get_font(const unsigned int *const font, const unsigned int w, const unsigned int h, const unsigned int paddingx, const unsigned int paddingy, const bool variable_size=true) { CImgList res = CImgList(256,w,h,1,3).insert(CImgList(256,w,h,1,1)); const unsigned int *ptr = font; unsigned int m = 0, val = 0; for (unsigned int y=0; y>=1; if (!m) { m = 0x80000000; val = *(ptr++); } CImg& img = res[x/w], &mask = res[x/w+256]; unsigned int xm = x%w; img(xm,y,0) = img(xm,y,1) = img(xm,y,2) = mask(xm,y,0) = (T)((val&m)?1:0); } if (variable_size) res.crop_font(); if (paddingx || paddingy) cimglist_for(res,l) res[l].resize(res[l].dimx()+paddingx, res[l].dimy()+paddingy,1,-100,0); return res; } //! In-place version of the previous function. CImgList& font(const unsigned int *const font, const unsigned int w, const unsigned int h, const unsigned int paddingx, const unsigned int paddingy, const bool variable_size=true) { return get_font(font,w,h,paddingx,paddingy,variable_size).assign_to(*this); } //! Return a CImg pre-defined font with desired size /** \param font_height = height of the desired font (can be 11,13,24,38 or 57) \param fixed_size = tell if the font has a fixed or variable width. **/ static CImgList get_font(const unsigned int font_width, const bool variable_size=true) { if (font_width<=11) { static CImgList font7x11, nfont7x11; if (!variable_size && !font7x11) font7x11 = get_font(cimg::font7x11,7,11,1,0,false); if (variable_size && !nfont7x11) nfont7x11 = get_font(cimg::font7x11,7,11,1,0,true); return variable_size?nfont7x11:font7x11; } if (font_width<=13) { static CImgList font10x13, nfont10x13; if (!variable_size && !font10x13) font10x13 = get_font(cimg::font10x13,10,13,1,0,false); if (variable_size && !nfont10x13) nfont10x13 = get_font(cimg::font10x13,10,13,1,0,true); return variable_size?nfont10x13:font10x13; } if (font_width<=17) { static CImgList font8x17, nfont8x17; if (!variable_size && !font8x17) font8x17 = get_font(cimg::font8x17,8,17,1,0,false); if (variable_size && !nfont8x17) nfont8x17 = get_font(cimg::font8x17,8,17,1,0,true); return variable_size?nfont8x17:font8x17; } if (font_width<=19) { static CImgList font10x19, nfont10x19; if (!variable_size && !font10x19) font10x19 = get_font(cimg::font10x19,10,19,2,0,false); if (variable_size && !nfont10x19) nfont10x19 = get_font(cimg::font10x19,10,19,2,0,true); return variable_size?nfont10x19:font10x19; } if (font_width<=24) { static CImgList font12x24, nfont12x24; if (!variable_size && !font12x24) font12x24 = get_font(cimg::font12x24,12,24,2,0,false); if (variable_size && !nfont12x24) nfont12x24 = get_font(cimg::font12x24,12,24,2,0,true); return variable_size?nfont12x24:font12x24; } if (font_width<=32) { static CImgList font16x32, nfont16x32; if (!variable_size && !font16x32) font16x32 = get_font(cimg::font16x32,16,32,2,0,false); if (variable_size && !nfont16x32) nfont16x32 = get_font(cimg::font16x32,16,32,2,0,true); return variable_size?nfont16x32:font16x32; } if (font_width<=38) { static CImgList font19x38, nfont19x38; if (!variable_size && !font19x38) font19x38 = get_font(cimg::font19x38,19,38,3,0,false); if (variable_size && !nfont19x38) nfont19x38 = get_font(cimg::font19x38,19,38,3,0,true); return variable_size?nfont19x38:font19x38; } static CImgList font29x57, nfont29x57; if (!variable_size && !font29x57) font29x57 = get_font(cimg::font29x57,29,57,5,0,false); if (variable_size && !nfont29x57) nfont29x57 = get_font(cimg::font29x57,29,57,5,0,true); return variable_size?nfont29x57:font29x57; } //! In-place version of the previous function. CImgList& font(const unsigned int font_width, const bool variable_size=true) { return get_font(font_width,variable_size).assign_to(*this); } //! Display the current CImgList instance in an existing CImgDisplay window (by reference). /** This function displays the list images of the current CImgList instance into an existing CImgDisplay window. Images of the list are concatenated in a single temporarly image for visualization purposes. The function returns immediately. \param disp : reference to an existing CImgDisplay instance, where the current image list will be displayed. \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \return A reference to the current CImgList instance is returned. **/ const CImgList& display(CImgDisplay& disp, const char axe='x', const char align='c') const { get_append(axe,align).display(disp); return *this; } //! Display the current CImgList instance in a new display window. /** This function opens a new window with a specific title and displays the list images of the current CImgList instance into it. Images of the list are concatenated in a single temporarly image for visualization purposes. The function returns when a key is pressed or the display window is closed by the user. \param title : specify the title of the opening display window. \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \param min_size : specify the minimum size of the opening display window. Images having dimensions below this size will be upscaled. \param max_size : specify the maximum size of the opening display window. Images having dimensions above this size will be downscaled. \return A reference to the current CImgList instance is returned. **/ const CImgList& display(const char* title, const char axe='x', const char align='c', const int min_size=128, const int max_size=1024, const int print_flag=1) const { if (is_empty()) throw CImgInstanceException("CImgList<%s>::display() : Instance list (%u,%u) is empty.", pixel_type(),size,data); const CImg visu = get_append(axe,align); CImgDisplay disp; unsigned int w = visu.width+(visu.depth>1?visu.depth:0), h = visu.height+(visu.depth>1?visu.depth:0), XYZ[3]; print(title,print_flag); const unsigned int dmin = cimg::min(w,h), minsiz = min_size>=0?min_size:(-min_size)*dmin/100; if (dmin=0?max_size:(-max_size)*dmax/100; if (dmax>maxsiz) { w=w*maxsiz/dmax; w+=(w==0); h=h*maxsiz/dmax; h+=(h==0); } disp.assign(w,h,title,1,3); XYZ[0] = visu.width/2; XYZ[1] = visu.height/2; XYZ[2] = visu.depth/2; while (!disp.is_closed && !disp.key) visu.get_coordinates(1,disp,XYZ); return *this; } //! Display the current CImgList instance in a new display window. /** This function opens a new window and displays the list images of the current CImgList instance into it. Images of the list are concatenated in a single temporarly image for visualization purposes. The function returns when a key is pressed or the display window is closed by the user. \param axe : specify the axe for image concatenation. Can be 'x','y','z' or 'v'. \param align : specify the alignment for image concatenation. Can be 'p' (top), 'c' (center) or 'n' (bottom). \param min_size : specify the minimum size of the opening display window. Images having dimensions below this size will be upscaled. \param max_size : specify the maximum size of the opening display window. Images having dimensions above this size will be downscaled. \return A reference to the current CImgList instance is returned. **/ const CImgList& display(const char axe='x', const char align='c', const int min_size=128, const int max_size=1024, const int print_flag=1) const { char title[256] = { 0 }; std::sprintf(title,"CImgList<%s>",pixel_type()); return display(title,axe,align,min_size,max_size,print_flag); } }; /* #----------------------------------------- # # # # Complete previously defined functions # # # #------------------------------------------ */ namespace cimg { //! Display a dialog box, where a user can click standard buttons. /** Up to 6 buttons can be defined in the dialog window. This function returns when a user clicked one of the button or closed the dialog window. \param title = Title of the dialog window. \param msg = Main message displayed inside the dialog window. \param button1_txt = Label of the 1st button. \param button2_txt = Label of the 2nd button. \param button3_txt = Label of the 3rd button. \param button4_txt = Label of the 4th button. \param button5_txt = Label of the 5th button. \param button6_txt = Label of the 6th button. \param logo = Logo image displayed at the left of the main message. This parameter is optional. \param centering = Tell to center the dialog window on the screen. \return The button number (from 0 to 5), or -1 if the dialog window has been closed by the user. \note If a button text is set to 0, then the corresponding button (and the followings) won't appear in the dialog box. At least one button is necessary. **/ template inline int dialog(const char *title, const char *msg, const char *button1_txt, const char *button2_txt, const char *button3_txt, const char *button4_txt, const char *button5_txt, const char *button6_txt, const CImg& logo, const bool centering = false) { #if cimg_display_type!=0 const unsigned char black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 }; // Create buttons and canvas graphics CImgList buttons, cbuttons, sbuttons; if (button1_txt) { buttons.insert(CImg().draw_text(button1_txt,0,0,black,gray,13)); if (button2_txt) { buttons.insert(CImg().draw_text(button2_txt,0,0,black,gray,13)); if (button3_txt) { buttons.insert(CImg().draw_text(button3_txt,0,0,black,gray,13)); if (button4_txt) { buttons.insert(CImg().draw_text(button4_txt,0,0,black,gray,13)); if (button5_txt) { buttons.insert(CImg().draw_text(button5_txt,0,0,black,gray,13)); if (button6_txt) { buttons.insert(CImg().draw_text(button6_txt,0,0,black,gray,13)); } } } } } } if (!buttons.size) throw CImgArgumentException("cimg::dialog() : No buttons have been defined. At least one is necessary"); unsigned int bw = 0, bh = 0; cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l].width); bh = cimg::max(bh,buttons[l].height); } bw+=8; bh+=8; if (bw<64) bw=64; if (bw>128) bw=128; if (bh<24) bh=24; if (bh>48) bh=48; CImg button(bw,bh,1,3); button.draw_rectangle(0,0,bw-1,bh-1,gray); button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white); button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black); button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2); CImg sbutton(bw,bh,1,3); sbutton.draw_rectangle(0,0,bw-1,bh-1,gray); sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black); sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black); sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white); sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black); sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2); sbutton.draw_line(4,4,bw-5,4,black,1.0f,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1.0f,0xAAAAAAAA,false); sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1.0f,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1.0f,0xAAAAAAAA,false); CImg cbutton(bw,bh,1,3); cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray); cbutton.draw_line(4,4,bw-5,4,black,1.0f,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1.0f,0xAAAAAAAA,false); cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1.0f,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1.0f,0xAAAAAAAA,false); cimglist_for(buttons,ll) { cbuttons.insert(CImg(cbutton).draw_image(buttons[ll],1+(bw-buttons[ll].dimx())/2,1+(bh-buttons[ll].dimy())/2)); sbuttons.insert(CImg(sbutton).draw_image(buttons[ll],(bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2)); buttons[ll] = CImg(button).draw_image(buttons[ll],(bw-buttons[ll].dimx())/2,(bh-buttons[ll].dimy())/2); } CImg canvas; if (msg) canvas = CImg().draw_text(msg,0,0,black,gray,13); const unsigned int bwall = (buttons.size-1)*(12+bw) + bw, w = cimg::max(196U,36+logo.width+canvas.width, 24+bwall), h = cimg::max(96U,36+canvas.height+bh,36+logo.height+bh), lx = 12 + (canvas.data?0:((w-24-logo.width)/2)), ly = (h-12-bh-logo.height)/2, tx = lx+logo.width+12, ty = (h-12-bh-canvas.height)/2, bx = (w-bwall)/2, by = h-12-bh; if (canvas.data) canvas = CImg(w,h,1,3). draw_rectangle(0,0,w-1,h-1,gray). draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black). draw_image(canvas,tx,ty); else canvas = CImg(w,h,1,3). draw_rectangle(0,0,w-1,h-1,gray). draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white). draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black); if (logo.data) canvas.draw_image(logo,lx,ly); unsigned int xbuttons[6]; cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(buttons[lll],xbuttons[lll],by); } // Open window and enter events loop CImgDisplay disp(canvas,title?title:" ",0,3,false,centering?true:false); if (centering) disp.move((CImgDisplay::screen_dimx()-disp.dimx())/2, (CImgDisplay::screen_dimy()-disp.dimy())/2); bool stopflag = false, refresh = false; int oselected = -1, oclicked = -1, selected = -1, clicked = -1; while (!disp.is_closed && !stopflag) { if (refresh) { if (clicked>=0) CImg(canvas).draw_image(cbuttons[clicked],xbuttons[clicked],by).display(disp); else { if (selected>=0) CImg(canvas).draw_image(sbuttons[selected],xbuttons[selected],by).display(disp); else canvas.display(disp); } refresh = false; } disp.wait(15); if (disp.is_resized) disp.resize(disp); if (disp.button&1) { oclicked = clicked; clicked = -1; cimglist_for(buttons,l) if (disp.mouse_y>=(int)by && disp.mouse_y<(int)(by+bh) && disp.mouse_x>=(int)xbuttons[l] && disp.mouse_x<(int)(xbuttons[l]+bw)) { clicked = selected = l; refresh = true; } if (clicked!=oclicked) refresh = true; } else if (clicked>=0) stopflag = true; if (disp.key) { oselected = selected; switch (disp.key) { case cimg::keyESC: selected=-1; stopflag=true; break; case cimg::keyENTER: if (selected<0) selected = 0; stopflag = true; break; case cimg::keyTAB: case cimg::keyARROWRIGHT: case cimg::keyARROWDOWN: selected = (selected+1)%buttons.size; break; case cimg::keyARROWLEFT: case cimg::keyARROWUP: selected = (selected+buttons.size-1)%buttons.size; break; } disp.key = 0; if (selected!=oselected) refresh = true; } } if (!disp) selected = -1; return selected; #else std::fprintf(stderr,"<%s>\n\n%s\n\n",title,msg); return -1+0*(int)(button1_txt-button2_txt+button3_txt-button4_txt+button5_txt-button6_txt+logo.width+(int)centering); #endif } inline int dialog(const char *title, const char *msg, const char *button1_txt, const char *button2_txt, const char *button3_txt, const char *button4_txt, const char *button5_txt, const char *button6_txt, const bool centering) { return dialog(title,msg,button1_txt,button2_txt,button3_txt,button4_txt,button5_txt,button6_txt, CImg::get_logo40x38(),centering); } // Inner routine used by the Marching cube algorithm template inline int _marching_cubes_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) { switch (edge) { case 0: return indices1(x,y,0); case 1: return indices1(nx,y,1); case 2: return indices1(x,ny,0); case 3: return indices1(x,y,1); case 4: return indices2(x,y,0); case 5: return indices2(nx,y,1); case 6: return indices2(x,ny,0); case 7: return indices2(x,y,1); case 8: return indices1(x,y,2); case 9: return indices1(nx,y,2); case 10: return indices1(nx,ny,2); case 11: return indices1(x,ny,2); } return 0; } //! Polygonize an implicit function // This function uses the Marching Cubes Tables published on the web page : // http://astronomy.swin.edu.au/~pbourke/modelling/polygonise/ template inline void marching_cubes(const tfunc& func, const float isovalue, const float x0, const float y0, const float z0, const float x1, const float y1, const float z1, const float resx, const float resy, const float resz, CImgList& points, CImgList& primitives, const bool invert_faces) { static unsigned int edges[256] = { 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90, 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 }; static int triangles[256][16] = { {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, {3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, {3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, {3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, {9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, {8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, {3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, {1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, {4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, {5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, {9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, {0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, {2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, {5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, {5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, {9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, {1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, {8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, {2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, {7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, {2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, {5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, {9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, {2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, {6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, {3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, {6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, {6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, {8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, {7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, {3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, {5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, {0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, {9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, {8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, {5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, {0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, {6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, {1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, {0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, {3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, {6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, {9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, {8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, {3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, {6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, {0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, {7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, {7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, {2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, {1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, {8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, {0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, {7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, {2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, {7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, {2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, {1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, {0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, {7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, {6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, {8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, {9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, {6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, {4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, {8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, {1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, {8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, {4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, {5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, {9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, {6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, {7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, {3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, {7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, {3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, {6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, {9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, {1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, {4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, {7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, {6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, {0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, {6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, {0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, {6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, {5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, {9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, {1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, {1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, {0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, {5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, {9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, {7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, {2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, {8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, {9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, {9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, {1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, {9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, {9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, {0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, {2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, {0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, {0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, {9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, {5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, {3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, {5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, {8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, {0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, {9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, {1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, {3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, {4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, {9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, {2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, {9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, {3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, {1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, {4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, {4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, {0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, {3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, {3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, {0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, {9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, {1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 3, 8, -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} }; const unsigned int nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1, nz = (unsigned int)((z1-z0+1)/resz), nzm1 = nz-1; if (!nxm1 || !nym1 || !nzm1) return; CImg indices1(nx,ny,1,3,-1), indices2(indices1); CImg values1(nx,ny), values2(nx,ny); float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0; // Fill the first plane with function values Y=y0; cimg_forY(values1,y) { X = x0; cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=resx; } Y+=resy; } // Run Marching Cubes algorithm Z = z0; nZ = Z + resz; for (unsigned int zi=0; zi::vector(Xi,Y,Z)); } if ((edge&2) && indices1(nxi,yi,1)<0) { const float Yi = Y + (isovalue-val1)*resy/(val2-val1); indices1(nxi,yi,1) = points.size; points.insert(CImg::vector(nX,Yi,Z)); } if ((edge&4) && indices1(xi,nyi,0)<0) { const float Xi = X + (isovalue-val3)*resx/(val2-val3); indices1(xi,nyi,0) = points.size; points.insert(CImg::vector(Xi,nY,Z)); } if ((edge&8) && indices1(xi,yi,1)<0) { const float Yi = Y + (isovalue-val0)*resy/(val3-val0); indices1(xi,yi,1) = points.size; points.insert(CImg::vector(X,Yi,Z)); } if ((edge&16) && indices2(xi,yi,0)<0) { const float Xi = X + (isovalue-val4)*resx/(val5-val4); indices2(xi,yi,0) = points.size; points.insert(CImg::vector(Xi,Y,nZ)); } if ((edge&32) && indices2(nxi,yi,1)<0) { const float Yi = Y + (isovalue-val5)*resy/(val6-val5); indices2(nxi,yi,1) = points.size; points.insert(CImg::vector(nX,Yi,nZ)); } if ((edge&64) && indices2(xi,nyi,0)<0) { const float Xi = X + (isovalue-val7)*resx/(val6-val7); indices2(xi,nyi,0) = points.size; points.insert(CImg::vector(Xi,nY,nZ)); } if ((edge&128) && indices2(xi,yi,1)<0) { const float Yi = Y + (isovalue-val4)*resy/(val7-val4); indices2(xi,yi,1) = points.size; points.insert(CImg::vector(X,Yi,nZ)); } if ((edge&256) && indices1(xi,yi,2)<0) { const float Zi = Z+ (isovalue-val0)*resz/(val4-val0); indices1(xi,yi,2) = points.size; points.insert(CImg::vector(X,Y,Zi)); } if ((edge&512) && indices1(nxi,yi,2)<0) { const float Zi = Z + (isovalue-val1)*resz/(val5-val1); indices1(nxi,yi,2) = points.size; points.insert(CImg::vector(nX,Y,Zi)); } if ((edge&1024) && indices1(nxi,nyi,2)<0) { const float Zi = Z + (isovalue-val2)*resz/(val6-val2); indices1(nxi,nyi,2) = points.size; points.insert(CImg::vector(nX,nY,Zi)); } if ((edge&2048) && indices1(xi,nyi,2)<0) { const float Zi = Z + (isovalue-val3)*resz/(val7-val3); indices1(xi,nyi,2) = points.size; points.insert(CImg::vector(X,nY,Zi)); } // Create triangles for (int *triangle=triangles[configuration]; *triangle!=-1; ) { const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++); const tf i0 = (tf)(_marching_cubes_indice(p0,indices1,indices2,xi,yi,nxi,nyi)), i1 = (tf)(_marching_cubes_indice(p1,indices1,indices2,xi,yi,nxi,nyi)), i2 = (tf)(_marching_cubes_indice(p2,indices1,indices2,xi,yi,nxi,nyi)); if (invert_faces) primitives.insert(CImg::vector(i0,i1,i2)); else primitives.insert(CImg::vector(i0,i2,i1)); } } } } cimg::swap(values1,values2); cimg::swap(indices1,indices2); } } // Inner routine used by the Marching square algorithm template inline int _marching_squares_indice(const unsigned int edge, const CImg& indices1, const CImg& indices2, const unsigned int x, const unsigned int nx) { switch (edge) { case 0: return (int)indices1(x,0); case 1: return (int)indices1(nx,1); case 2: return (int)indices2(x,0); case 3: return (int)indices1(x,1); } return 0; } //! Polygonize an implicit 2D function by the marching squares algorithm template inline void marching_squares(const tfunc& func, const float isovalue, const float x0, const float y0, const float x1, const float y1, const float resx, const float resy, CImgList& points, CImgList& primitives) { static unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 }; static int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 }, { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 }, { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 }, { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } }; const unsigned int nx = (unsigned int)((x1-x0+1)/resx), nxm1 = nx-1, ny = (unsigned int)((y1-y0+1)/resy), nym1 = ny-1; if (!nxm1 || !nym1) return; CImg indices1(nx,1,1,2,-1), indices2(nx,1,1,2); CImg values1(nx), values2(nx); float X = 0, Y = 0, nX = 0, nY = 0; // Fill first line with values cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=resx; } // Run the marching squares algorithm Y = y0; nY = Y + resy; for (unsigned int yi=0, nyi=1; yi::vector(Xi,Y)); } if ((edge&2) && indices1(nxi,1)<0) { const float Yi = Y + (isovalue-val1)*resy/(val2-val1); indices1(nxi,1) = points.size; points.insert(CImg::vector(nX,Yi)); } if ((edge&4) && indices2(xi,0)<0) { const float Xi = X + (isovalue-val3)*resx/(val2-val3); indices2(xi,0) = points.size; points.insert(CImg::vector(Xi,nY)); } if ((edge&8) && indices1(xi,1)<0) { const float Yi = Y + (isovalue-val0)*resy/(val3-val0); indices1(xi,1) = points.size; points.insert(CImg::vector(X,Yi)); } // Create segments for (int *segment=segments[configuration]; *segment!=-1; ) { const unsigned int p0 = *(segment++), p1 = *(segment++); const tf i0 = (tf)(_marching_squares_indice(p0,indices1,indices2,xi,nxi)), i1 = (tf)(_marching_squares_indice(p1,indices1,indices2,xi,nxi)); primitives.insert(CImg::vector(i0,i1)); } } } values1.swap(values2); indices1.swap(indices2); } } // End of cimg:: namespace } // End of cimg_library:: namespace } #ifdef cimg_use_visualcpp6 #undef std #endif #ifdef cimg_redefine_min #define min(a,b) (((a)<(b))?(a):(b)) #endif #ifdef cimg_redefine_max #define max(a,b) (((a)>(b))?(a):(b)) #endif #endif // Local Variables: // mode: c++ // End: osra-2.1.3/src/osra_labels.h0000664000175000017500000002260714115175251014404 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_labels.h // // declares functions dealing with atomic labels // #ifndef OSRA_LABELS_H #define OSRA_LABELS_H #include #include #include extern "C" { #include } #include "osra.h" // struct: letters_s // character found as part of atomic label struct letters_s { // double: x,y,r // coordinates of the center and radius of the circumscribing circle double x, y, r; // int: min_x, min_y, max_x, max_y // box coordinates int min_x, min_y,max_x,max_y; // char: a // character char a; // bool: free // whether or not it was already assign to an existing atomic label bool free; // pointer: curve // pointer to the curve found by Potrace const potrace_path_t *curve; }; // typedef: letters_t // defines letters_t type based on letters_s struct typedef struct letters_s letters_t; // struct: label_s // atomic label struct label_s { // doubles: x1,y1, x2, y2, r1, r2 // central coordinates and circumradii for the first and last characters double x1, y1, r1, x2, y2, r2; // int: min_x, min_y, max_x, max_y // box coordinates int min_x, min_y,max_x,max_y; // string: a // atomic label string std::string a; // array: n // vector of character indices comprising the atomic label std::vector n; }; // typedef: label_t // defines label_t type based on label_s struct typedef struct label_s label_t; //struct: lbond_s //pairs of characters used for constucting atomic labels in struct lbond_s { //int: a,b // indices of first and second character in a pair int a, b; //double: x // x-coordinate of the first character double x; //bool: exists //pair of characters is available bool exists; }; //typedef: lbond_t //defines lbond_t type based on lbond_s struct typedef struct lbond_s lbond_t; // // Section: Functions // // Function: assemble_labels() // // assembles characters into a string for a superatom label // // Parameters: // letters - a vector of recognized characters // n_letters - the number of recognized characters // label - a reference to the vector which will contain superatom labels // // Returns: // number of superatom labels int assemble_labels(std::vector &letters, int n_letters, std::vector &label); // Function: find_chars() // // searches for perspective characters in the image and calls OCR routines // // Parameters: // p - vectorized output of Potrace routines // orig - original image // letters - vector which will contain recognized characters // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - number of bonds // height - image height // width - image width // bgColor - background color // THRESHOLD - black-white binarization threshold // max_font_width - maximum font width for the specific resolution in pixels // max_font_height - maximum font height for the specific resolution in pixels // real_font_width - detected font width // real_font_height - detected font height // verbose - flag for verbose output // recognized_chars - user-supplied ocr filter // // Returns: // number of recognized characters int find_chars(const potrace_path_t * p, const Image &orig, std::vector &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, ColorGray &bgColor, double THRESHOLD, int max_font_width, int max_font_height, int &real_font_width, int &real_font_height, bool verbose, const std::string &recognized_chars); // Function: find_numbers() // // searches for numbers 0..9 in the image and calls OCR routines // // Parameters: // p - vectorized output of Potrace routines // orig - original image // letters - vector which will contain recognized characters // atom - vector of atoms // bond - vector of bonds // n_atom - number of atoms // n_bond - number of bonds // height - image height // width - image width // bgColor - background color // THRESHOLD - black-white binarization threshold // n_letters - number of previously recognized characters // // Returns: // number of recognized characters int find_numbers(const potrace_path_t * p, const Image &orig, std::vector &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, ColorGray &bgColor, double THRESHOLD, int n_letters); // Function: find_plus_minus() // // Detects plus and minus signs in atomic charge labels // // Parameters: // p - Potrace vectorization output // image - original image // bgColor - background color // THRESHOLD - black-white binarization threshold // letters - a vector of atomic label characters // atom - a vector of atoms // bond - a vector of bonds // n_atom - number of atoms // n_bond - number of bonds // height - image height // width - image width // max_font_height - maximum font height for a given resolution // max_font_width - maximum font width for a given resolution // n_letters - number of characters // // Returns: // new number of characters int find_plus_minus(const potrace_path_t *p, const Image &image, ColorGray &bgColor, double THRESHOLD, std::vector &letters, std::vector &atom, std::vector &bond, int n_atom, int n_bond, int height, int width, int max_font_height, int max_font_width, int n_letters, double avg_bond_length); // Function: clean_unrecognized_characters() // // Attempts to assemble collections of small bonds which did not pass OCR routines // into an unspecified atomic label "*" // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // real_font_height - font height as determined by preceeding OCR routines // real_font_width - font width as determined by preceeding OCR routines // size - minimum number of bonds to constitute a perspective character // letters - vector of recognized characters from OCR // n_letters - number of recognized characters // // Returns: // new value for n_letters int clean_unrecognized_characters(std::vector &bond, int n_bond, const std::vector &atom, int real_font_height, int real_font_width, unsigned int size, std::vector &letters, int n_letters); // Function: remove_small_terminal_bonds() // // Small terminal bonds are often a result of unrecognized atomic labels // This function replaces such bonds with "Xx" atomic label // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // avg - average bond length void remove_small_terminal_bonds(std::vector &bond, int n_bond, std::vector &atom, double avg); // Function: remove_small_bonds() // // Removes very small single bonds or replaces them, if stand-alone and next to a character and vertical // with a character "l" // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // letters - vector of characters // n_letters - number of characters // max_font_height - maximum font height // min_font_height - minimum font height // avg - average bond length // // Returns: // new value for n_letters int remove_small_bonds(std::vector &bond, int n_bond, const std::vector &atom, std::vector &letters, int n_letters, int max_font_height, int min_font_height, double avg); // Function: find_fused_chars() // // Attempts to recognize characters fused to a bond // // Parameters: // bond - vector of bonds // n_bond - number of bonds // atom - vector of atoms // letters - vector of characters // n_letters - number of characters // max_font_height - maximum font height // max_font_width - maximum font width // dummy - if set, character to substitute for OCR results // orig - original image // bgColor - background color // THRESHOLD - black-white threshold for image binarization // size - minimum number of bonds which can constitute a character // verbose - flag for verbose output // recognized_chars - user-supplied ocr filter // // Returns: // new value for n_letters int find_fused_chars(std::vector &bond, int n_bond, std::vector &atom, std::vector &letters, int n_letters, int max_font_height, int max_font_width, char dummy, const Image &orig, const ColorGray &bgColor, double THRESHOLD, unsigned int size, bool verbose, const std::string &recognized_chars); #endif osra-2.1.3/src/osra_thin.h0000664000175000017500000000460114115175251014076 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_thin.h // // Image thinning routines and noise factor computation // #include using namespace Magick; // // Section: Functions // // Function: noise_factor() // // computes attributes of line thickness histogram // // Parameters: // image - image to be processed // width - image width // height - image height // bgColor - background color // THRESHOLD_BOND - black-white binarization threshold // resolution - resolution for which we're performing processing // max - position of the maximum of the thickness histogram (most common thickness) // nf45 - ratio of number of lines with thickness 4 to the number of lines with thickness 5 // // Returns: // Ratio of the number of lines with thickness 2 to number of lines of thickness 3 // or, if max == 2, ratio of the count of lines with thickness 1 to number of lines of thickness 2 // or, if max == 1, ratio of the count of lines with thickness 2 to number of lines of thickness 1 double noise_factor(const Image &image, int width, int height, const ColorGray &bgColor, double THRESHOLD_BOND, int resolution, int &max, double &nf45); // Function: thin_image() // // Performs image thinning based on Rosenfeld's algorithm // // Parameters: // box - original image // THRESHOLD_BOND - black-white binarization threshold // bgColor - background color // // Returns: // Thinned image Image thin_image(const Image &box, double THRESHOLD_BOND, const ColorGray &bgColor); osra-2.1.3/src/osra_grayscale.h0000664000175000017500000000342714115175251015113 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // Header: osra_grayscale.h // // Declares grayscale conversion functions // #include using namespace Magick; // // Section: Functions // // Function: getBgColor() // // Detects the backgroun color of the image // // Parameters: // image - a reference to the image object // // Returns: // a Color object corresponding to the background color const Color getBgColor(const Image &image); // Function: convert_to_gray() // // Converts image to grayscale // // Parameters: // image - reference to Image object // invert - flag set if the image is white-on-black // adaptive - flag set if adaptive thresholding is enforced // verbose - flag set if verbose reporting is on // // Returns: // a boolean flag indicating whether adaptive thresholding is indicated bool convert_to_gray(Image &image, bool invert, bool adaptive, bool verbose); osra-2.1.3/src/osra_thin.cpp0000664000175000017500000002631714115175251014441 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #include "osra_common.h" #include "osra_thin.h" #include /*------------------- ThinImage - Thin binary image. --------------------------- * * * Description: * Thins the supplied binary image using Rosenfeld's parallel * thinning algorithm. * * On Entry: * image = Image to thin. * *------------------------------------------------------------------------------- */ /* Direction masks: */ /* N S W E */ static unsigned int masks[] = { 0200, 0002, 0040, 0010 }; /* True if pixel neighbor map indicates the pixel is 8-simple and */ /* not an end point and thus can be deleted. The neighborhood */ /* map is defined as an integer of bits abcdefghi with a non-zero */ /* bit representing a non-zero pixel. The bit assignment for the */ /* neighborhood is: */ /* */ /* a b c */ /* d e f */ /* g h i */ static unsigned char todelete[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 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, 1, 1, 1, 0, 0, 1, 1, 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, 1, 0, 0, 1, 1, 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, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 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, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; void thin1(unsigned char * const ptr, unsigned int xsize, unsigned int ysize) { unsigned char *y_ptr, *y1_ptr; unsigned char bg_color = 0, colour = 1; unsigned int x, y; /* Pixel location */ unsigned int i; /* Pass index */ unsigned int pc = 0; /* Pass count */ unsigned int count = 1; /* Deleted pixel count */ unsigned int p, q; /* Neighborhood maps of adjacent*/ /* cells */ unsigned char *qb; /* Neighborhood maps of previous*/ /* scanline */ unsigned int m; /* Deletion direction mask */ qb = (unsigned char*) malloc(xsize * sizeof(unsigned char)); qb[xsize - 1] = 0; /* Used for lower-right pixel */ while (count) /* Scan image while deletions */ { pc++; count = 0; for (i = 0; i < 4; i++) { m = masks[i]; /* Build initial previous scan buffer. */ p = (ptr[0] == colour); for (x = 0; x < xsize - 1; x++) qb[x] = (unsigned char) (p = ((p << 1) & 0006) | (unsigned int) (ptr[x + 1] == colour)); /* Scan image for pixel deletion candidates. */ y_ptr = ptr; y1_ptr = ptr + xsize; for (y = 0; y < ysize - 1; y++, y_ptr += xsize, y1_ptr += xsize) { q = qb[0]; p = ((q << 2) & 0330) | (y1_ptr[0] == colour); for (x = 0; x < xsize - 1; x++) { q = qb[x]; p = ((p << 1) & 0666) | ((q << 3) & 0110) | (unsigned int) (y1_ptr[x + 1] == colour); qb[x] = (unsigned char) p; if (((p & m) == 0) && todelete[p]) { count++; y_ptr[x] = bg_color; /* delete the pixel */ } } /* Process right edge pixel. */ p = (p << 1) & 0666; if ((p & m) == 0 && todelete[p]) { count++; y_ptr[xsize - 1] = bg_color; } } /* Process bottom scan line. */ q = qb[0]; p = ((q << 2) & 0330); y_ptr = ptr + xsize * (ysize - 1); for (x = 0; x < xsize; x++) { q = qb[x]; p = ((p << 1) & 0666) | ((q << 3) & 0110); if ((p & m) == 0 && todelete[p]) { count++; y_ptr[x] = bg_color; } } } } free(qb); } Image thin_image(const Image &box, double THRESHOLD_BOND, const ColorGray &bgColor) { Image image(Geometry(box.columns(), box.rows()), "white"); image.type(GrayscaleType); unsigned int xsize = box.columns(); unsigned int ysize = box.rows(); unsigned char *ptr = (unsigned char*) malloc(xsize * ysize * sizeof(unsigned char)); for (unsigned int i = 0; i < xsize; i++) for (unsigned int j = 0; j < ysize; j++) ptr[i + j * xsize] = get_pixel(box, bgColor, i, j, THRESHOLD_BOND); if (xsize>1 && ysize>1) thin1(ptr, xsize, ysize); for (unsigned int i = 0; i < xsize; i++) for (unsigned int j = 0; j < ysize; j++) if (ptr[i + j * xsize] == 1) image.pixelColor(i, j, "black"); free(ptr); return (image); } double noise_factor(const Image &image, int width, int height, const ColorGray &bgColor, double THRESHOLD_BOND, int resolution, int &max, double &nf45) { int max_thick = 40; std::vector n(max_thick, 0); double nf; std::vector lines; for (int i = 0; i < width; i++) { int j = 0; while (j < height) { while (!get_pixel(image, bgColor, i, j, THRESHOLD_BOND) && j < height) j++; int l = 0; while (get_pixel(image, bgColor, i, j, THRESHOLD_BOND) && j < height) { l++; j++; } if (l < max_thick) n[l]++; lines.push_back(l); } } for (int i = 0; i < height; i++) { int j = 0; while (j < width) { while (!get_pixel(image, bgColor, j, i, THRESHOLD_BOND) && j < width) j++; int l = 0; while (get_pixel(image, bgColor, j, i, THRESHOLD_BOND) && j < width) { l++; j++; } if (l < max_thick) n[l]++; lines.push_back(l); } } /*for (int i = 0; i < height; i++) { int j = 0; while (j < width && i+j < height) { while (!get_pixel(image, bgColor, j, i+j, THRESHOLD_BOND) && j < width && i+j < height) j++; int l = 0; while (get_pixel(image, bgColor, j, i+j, THRESHOLD_BOND) && j < width && i+j < height) { l++; j++; } if (l < max_thick) n[l]++; lines.push_back(l); } } for (int i = 0; i < height; i++) { int j = width - 1; while (j >= 0 && i+(width-1-j) < height) { while (!get_pixel(image, bgColor, j, i+(width-1-j), THRESHOLD_BOND) && j >= 0 && i+(width-1-j) < height) j--; int l = 0; while (get_pixel(image, bgColor, j, i+(width-1-j), THRESHOLD_BOND) && j >= 0 && i+(width-1-j) < height) { l++; j--; } if (l < max_thick) n[l]++; lines.push_back(l); } } for (int i = 0; i < width; i++) { int j = 0; while (j < height && i+j < width) { while (!get_pixel(image, bgColor, i+j, j, THRESHOLD_BOND) && j < height && i+j < width) j++; int l = 0; while (get_pixel(image, bgColor, i+j, j, THRESHOLD_BOND) && j < height && i+j < width) { l++; j++; } if (l < max_thick) n[l]++; lines.push_back(l); } } for (int i = 0; i < width; i++) { int j = height - 1; while (j > 0 && i+(height-1-j) < width) { while (!get_pixel(image, bgColor, i+(height-1-j), j, THRESHOLD_BOND) && j > 0 && i+(height-1-j) < width) j--; int l = 0; while (get_pixel(image, bgColor, i+(height-1-j), j, THRESHOLD_BOND) && j > 0 && i+(height-1-j) < width) { l++; j--; } if (l < max_thick) n[l]++; lines.push_back(l); } }*/ double max_v = 0; max = 1; sort(lines.begin(),lines.end()); for (int l = 1; l < max_thick; l++) { //cout << l << " " << n[l] << endl; if (n[l] > max_v) { max_v = n[l]; max = l; } } //if (lines.size() > 1) // max = lines[lines.size() / 2]; if (max > 2) nf = n[2] / n[3]; else if (max == 2) nf = n[1] / n[2]; else nf = n[2] / n[1]; if (n[5]!=0) nf45=n[4]/n[5]; else nf45=0; return (nf); } osra-2.1.3/src/osra_openbabel.h0000664000175000017500000001317114115175251015065 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ #ifndef OSRA_OPENBABEL_H #define OSRA_OPENBABEL_H #include // std::string #include // std::map #include // std::vector #include "osra.h" #include "osra_segment.h" // Header: osra_openbabel.h // // Defines types and functions for OSRA OpenBabel module. // //struct: molecule_statistics_s // contains the statistical information about molecule used for analysis of recognition accuracy struct molecule_statistics_s { // unsigned int: rotors // number of rotors in molecule unsigned int rotors; // unsigned int: num_fragments // number of contiguous fragments in molecule unsigned int fragments; // unsigned int: rings56 // accumulated number of 5- and 6- rings in molecule unsigned int rings56; // unsigned int: rings456 // accumulated number of 4, 5, and 6-member rings in molecule unsigned int rings456; // unsigned int: num_atoms // number of atoms in molecule unsigned int num_atoms; // unsigned int: num_bonds // number of bonds in molecule unsigned int num_bonds; // unsigned int: num_organic_non_carbon_atoms // number of organic atoms which are not carbon or hydrogen unsigned int num_organic_non_carbon_atoms; // unsigned int: num_small_angles // number of bond angles smaller than 20 degrees unsigned int num_small_angles; }; // typedef: molecule_statistics_t // defines molecule_statistics_t type based on molecule_statistics_s struct typedef struct molecule_statistics_s molecule_statistics_t; // // Section: Functions // // Function: osra_openbabel_init() // // Performs OpenBabel library engine sanity check. Should be called at e.g. program startup. // // Returns: // non-zero value in case of error int osra_openbabel_init(); // Function: calculate_molecule_statistics() // // Converts vectors of atoms and bonds into a molecular object and calculates the molecule statistics. // Note: this function changes the atoms! // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - total number of bonds // avg_bond_length - average bond length as measured from the image (to be included into output if provided) // superatom - dictionary of superatom labels mapped to SMILES // verbose - print debug info // // Returns: // calculated molecule statistics molecule_statistics_t calculate_molecule_statistics( std::vector &atom, const std::vector &bond, int n_bond, double avg_bond_length, const std::map &superatom, bool verbose); // Function: get_formatted_structure() // // Converts vectors of atoms and bonds into a molecular object and encodes the molecular into a text presentation (SMILES, MOL file, ...), // specified by given format. // // Parameters: // atom - vector of atoms // bond - vector of bonds // n_bond - total number of bonds // format - output format for molecular representation - i.e. SMI, SDF // embedded_format - output format to be embedded into SDF (is only valid if output format is SDF); the only embedded formats supported now are "inchi", "smi", and "can" // molecule_statistics - the molecule statistics (returned to the caller) // confidence - confidence score (returned to the caller) // show_confidence - toggles confidence score inclusion into output // avg_bond_length - average bond length as measured from the image // scaled_avg_bond_length - average bond length scaled to the original resolution of the image // show_avg_bond_length - toggles average bond length inclusion into output // resolution - resolution at which image is being processed in DPI (to be included into output if provided) // page - page number (to be included into output if provided) // surrounding_box - the coordinates of surrounding image box that contains the structure (to be included into output if provided) // superatom - dictionary of superatom labels mapped to SMILES // verbose - print debug info // // Returns: // string containing SMILES, SDF or other representation of the molecule const std::string get_formatted_structure( std::vector &atom, const std::vector &bond, int n_bond, const std::string &format, const std::string &second_format, molecule_statistics_t &molecule_statistics, double &confidence, bool show_confidence, double avg_bond_length, double scaled_avg_bond_length, bool show_avg_bond_length, const int * const resolution, const int * const page, const box_t * const surrounding_box, const std::map &superatom, int n_letters, bool show_learning, int resolution_iteration, bool verbose, const std::vector& brackets); #endif osra-2.1.3/src/osra_structure.cpp0000664000175000017500000034266114115175251015542 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File: osra_structure.cpp // // Defines main structure recognition (molecular atoms and bonds) // recognition routines // #include // fabs(double) #include // FLT_MAX #include // std::cout #include #include "osra_common.h" #include "osra_structure.h" #include "osra_ocr.h" #include "osra_openbabel.h" void remove_disconnected_atoms(std::vector &atom, std::vector &bond, int n_atom, int n_bond) { for (int i = 0; i < n_atom; i++) { if (atom[i].exists) { atom[i].exists = false; for (int j = 0; j < n_bond; j++) { if ((bond[j].exists) && (i == bond[j].a || i == bond[j].b)) { atom[i].exists = true; } } } } } void remove_zero_bonds(std::vector &bond, int n_bond, std::vector &atom) { for (int i = 0; i < n_bond; i++) if (bond[i].exists) { for (int j = 0; j < n_bond; j++) if ((bond[j].exists) && (j != i) && ((bond[i].a == bond[j].a && bond[i].b == bond[j].b) || (bond[i].a == bond[j].b && bond[i].b == bond[j].a))) bond[j].exists = false; if (bond[i].a == bond[i].b) bond[i].exists = false; if (!atom[bond[i].a].exists || !atom[bond[i].b].exists) bond[i].exists = false; } } void collapse_doubleup_bonds(std::vector &bond, int n_bond) { for (int i = 0; i < n_bond; i++) if (bond[i].exists) for (int j = 0; j < n_bond; j++) if ((bond[j].exists) && (j != i) && ((bond[i].a == bond[j].a && bond[i].b == bond[j].b) || (bond[i].a == bond[j].b && bond[i].b == bond[j].a))) { bond[j].exists = false; bond[i].type++; } } void bond_end_swap(std::vector &bond, int i) { int t = bond[i].a; bond[i].a = bond[i].b; bond[i].b = t; } bool bonds_within_each_other(const std::vector &bond, int ii, int jj, const std::vector &atom) { int i, j; bool res = false; if (bond_length(bond, ii, atom) > bond_length(bond, jj, atom)) { i = ii; j = jj; } else { i = jj; j = ii; } double x1 = atom[bond[i].a].x; double x2 = atom[bond[i].b].x; double y1 = atom[bond[i].a].y; double y2 = atom[bond[i].b].y; double d1 = bond_length(bond, i, atom); double x3 = distance_from_bond_x_a(x1, y1, x2, y2, atom[bond[j].a].x, atom[bond[j].a].y); double x4 = distance_from_bond_x_a(x1, y1, x2, y2, atom[bond[j].b].x, atom[bond[j].b].y); if ((x3 + x4) / 2 > 0 && (x3 + x4) / 2 < d1) res = true; return (res); } bool no_white_space(int ai, int bi, int aj, int bj, const std::vector &atom, const Image &image, double threshold, const ColorGray &bgColor) { std::vector xx(4); double dx1 = atom[bi].x - atom[ai].x; double dy1 = atom[bi].y - atom[ai].y; double dx2 = atom[bj].x - atom[aj].x; double dy2 = atom[bj].y - atom[aj].y; double k1, k2; int s = 0, w = 0; int total_length = 0, white_length = 0; if (fabs(dx1) > fabs(dy1)) { xx[0] = atom[ai].x; xx[1] = atom[bi].x; xx[2] = atom[aj].x; xx[3] = atom[bj].x; std::sort(xx.begin(), xx.end()); k1 = dy1 / dx1; k2 = dy2 / dx2; int d = (dx1 > 0 ? 1 : -1); for (int x = int(atom[ai].x); x != int(atom[bi].x); x += d) if (x > xx[1] && x < xx[2]) { double p1 = (x - atom[ai].x) * k1 + atom[ai].y; double p2 = (x - atom[aj].x) * k2 + atom[aj].y; if (fabs(p2 - p1) < 1) continue; int dp = (p2 > p1 ? 1 : -1); bool white = false; for (int y = int(p1) + dp; y != int(p2); y += dp) { s++; if (get_pixel(image, bgColor, x, y, threshold) == 0) { w++; white = true; } } total_length++; if (white) white_length++; } } else { xx[0] = atom[ai].y; xx[1] = atom[bi].y; xx[2] = atom[aj].y; xx[3] = atom[bj].y; std::sort(xx.begin(), xx.end()); k1 = dx1 / dy1; k2 = dx2 / dy2; int d = (dy1 > 0 ? 1 : -1); for (int y = int(atom[ai].y); y != int(atom[bi].y); y += d) if (y > xx[1] && y < xx[2]) { double p1 = (y - atom[ai].y) * k1 + atom[ai].x; double p2 = (y - atom[aj].y) * k2 + atom[aj].x; if (fabs(p2 - p1) < 1) continue; int dp = (p2 > p1 ? 1 : -1); bool white = false; for (int x = int(p1) + dp; x != int(p2); x += dp) { s++; if (get_pixel(image, bgColor, x, y, threshold) == 0) { w++; white = true; } } total_length++; if (white) white_length++; } } //if (s == 0) return(true); //if ((1. * w) / s > WHITE_SPACE_FRACTION) return(false); if (total_length == 0) return (true); if ((1. * white_length) / total_length > 0.5) return (false); else return (true); } int find_wavy_bonds(std::vector &bond, int n_bond, const std::vector &atom, double avg) { int n_bond_orig = n_bond; const double t = 4; for (int i = 0; i < n_bond_orig; i++) if (bond[i].exists) { double l = bond_length(bond, i, atom); if (l > avg) avg = l; } for (int j = 0; j < n_bond_orig; j++) { bool found = false; std::set bag; int begin = 0; int end = 0; if (bond[j].exists)// && !bond[j].Small) { double l = bond_length(bond, j, atom); if (l < avg/4) { bag.insert(j); begin = j; end = j; found = true; } } while (found) { found = false; int mini = -1; double mind = t; double d1,d2,d3,d4; for (int i = 0; i < n_bond_orig; i++) if (bond[i].exists /*&& !bond[i].Small*/ && bag.find(i)==bag.end() && ((d1 = distance(atom[bond[end].a].x,atom[bond[end].a].y,atom[bond[i].a].x,atom[bond[i].a].y)) < mind || (d2 = distance(atom[bond[end].a].x,atom[bond[end].a].y,atom[bond[i].b].x,atom[bond[i].b].y)) < mind || (d3 = distance(atom[bond[end].b].x,atom[bond[end].b].y,atom[bond[i].a].x,atom[bond[i].a].y)) < mind || (d4 = distance(atom[bond[end].b].x,atom[bond[end].b].y,atom[bond[i].b].x,atom[bond[i].b].y)) < mind )) // (bond[end].a == bond[i].a || bond[end].a == bond[i].b || bond[end].b == bond[i].a || bond[end].b == bond[i].b)) { mind = std::min(std::min(d1, d2), std::min(d3, d4)); mini = i; } if (mini >= 0) { double l = bond_length(bond, mini, atom); if (l < avg/4) { bag.insert(mini); end = mini; found = true; } } mini = -1; mind = t; for (int i = 0; i < n_bond_orig; i++) if (bond[i].exists /*&& !bond[i].Small*/ && bag.find(i)==bag.end() && ((d1 = distance(atom[bond[begin].a].x,atom[bond[begin].a].y,atom[bond[i].a].x,atom[bond[i].a].y)) < mind || (d2 = distance(atom[bond[begin].a].x,atom[bond[begin].a].y,atom[bond[i].b].x,atom[bond[i].b].y)) < mind || (d3 = distance(atom[bond[begin].b].x,atom[bond[begin].b].y,atom[bond[i].a].x,atom[bond[i].a].y)) < mind || (d4 = distance(atom[bond[begin].b].x,atom[bond[begin].b].y,atom[bond[i].b].x,atom[bond[i].b].y)) < mind )) // (bond[begin].a == bond[i].a || bond[begin].a == bond[i].b || bond[begin].b == bond[i].a || bond[begin].b == bond[i].b)) { mind = std::min(std::min(d1, d2), std::min(d3, d4)); mini = i; } if (mini >= 0) { double l = bond_length(bond, mini, atom); if (l < avg/4) { bag.insert(mini); begin = mini; found = true; } } } if (bag.size() > 3) { double d1 = distance(atom[bond[begin].a].x,atom[bond[begin].a].y,atom[bond[end].a].x,atom[bond[end].a].y); double d2 = distance(atom[bond[begin].a].x,atom[bond[begin].a].y,atom[bond[end].b].x,atom[bond[end].b].y); double d3 = distance(atom[bond[begin].b].x,atom[bond[begin].b].y,atom[bond[end].a].x,atom[bond[end].a].y); double d4 = distance(atom[bond[begin].b].x,atom[bond[begin].b].y,atom[bond[end].b].x,atom[bond[end].b].y); double d = std::max(std::max(d1, d2), std::max(d3, d4)); if (d>0.75*avg && d<1.5*avg) { double xa = atom[bond[begin].a].x; double ya = atom[bond[begin].a].y; double xb = atom[bond[end].a].x; double yb = atom[bond[end].a].y; int a = bond[begin].a; int b = bond[end].a; if (d2 > d1) { xb = atom[bond[end].b].x; yb = atom[bond[end].b].y; b = bond[end].b; } if (d3 > std::max(d1,d2)) { xa = atom[bond[begin].b].x; ya = atom[bond[begin].b].y; xb = atom[bond[end].a].x; yb = atom[bond[end].a].y; a = bond[begin].b; b = bond[end].a; } if (d4 > std::max(d3, std::max(d1, d2))) { xa = atom[bond[begin].b].x; ya = atom[bond[begin].b].y; xb = atom[bond[end].b].x; yb = atom[bond[end].b].y; a = bond[begin].b; b = bond[end].b; } bool too_far = false; for(std::set::iterator i = bag.begin(); i != bag.end(); i++) { double da = fabs(distance_from_bond_y(xa,ya,xb,yb,atom[bond[*i].a].x, atom[bond[*i].a].y)); double db = fabs(distance_from_bond_y(xa,ya,xb,yb,atom[bond[*i].b].x, atom[bond[*i].b].y)); if (da > avg/4 || db > avg/4) { too_far = true; break; } } if (!too_far) // This looks like a wavy bond { bond_t nb(a,b,bond[begin].curve); nb.hash = true; nb.wedge = true; if (n_bond < MAX_ATOMS) { bond.push_back(nb); n_bond++; } for(std::set::iterator i = bag.begin(); i != bag.end(); i++) bond[*i].exists = false; } } } } return n_bond; } double skeletize(std::vector &atom, std::vector &bond, int n_bond, const Image &image, double threshold, const ColorGray &bgColor, double dist, double avg) { double thickness = 0; std::vector a; int n = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists && !bond[i].Small) { double l1 = bond_length(bond, i, atom); for (int j = 0; j < n_bond; j++) if (i != j && bond[j].exists && bonds_within_each_other(bond, i, j, atom) && !bond[j].Small) { double tt = distance_between_bonds(bond, i, j, atom); double tang = angle_between_bonds(bond, i, j, atom); if ((fabs(tang) > D_T_TOLERANCE && no_white_space(bond[i].a, bond[i].b, bond[j].a, bond[j].b, atom, image, threshold, bgColor) && tt < MAX_BOND_THICKNESS) || tt < dist) { double l2 = bond_length(bond, j, atom); a.push_back(tt); n++; if (l1 < l2) { bond[i].exists = false; bond[j].type = 1; if (bond[i].arom) bond[j].arom = true; if (l1 > avg / 2) { double ay = fabs(distance_from_bond_y(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].a].x, atom[bond[i].a].y)); double axa = fabs(distance_from_bond_x_a(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].a].x, atom[bond[i].a].y)); double axb = fabs(distance_from_bond_x_b(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].a].x, atom[bond[i].a].y)); if (tang > 0 && ay > axa) { atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[j].a].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[j].a].y) / 2; atom[bond[j].a].x = (atom[bond[i].a].x + atom[bond[j].a].x) / 2; atom[bond[j].a].y = (atom[bond[i].a].y + atom[bond[j].a].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].a].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].a].min_y); atom[bond[i].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].a].max_x); atom[bond[i].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].a].max_y); atom[bond[j].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].a].max_y); } if (tang < 0 && ay > axb) { atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[j].b].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[j].b].y) / 2; atom[bond[j].b].x = (atom[bond[i].a].x + atom[bond[j].b].x) / 2; atom[bond[j].b].y = (atom[bond[i].a].y + atom[bond[j].b].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].b].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].b].min_y); atom[bond[i].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].b].max_x); atom[bond[i].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].b].max_y); atom[bond[j].b].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].b].min_x); atom[bond[j].b].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].b].min_y); atom[bond[j].b].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].b].max_x); atom[bond[j].b].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].b].max_y); } double by = fabs(distance_from_bond_y(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].b].x, atom[bond[i].b].y)); double bxa = fabs(distance_from_bond_x_a(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].b].x, atom[bond[i].b].y)); double bxb = fabs(distance_from_bond_x_b(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].b].x, atom[bond[i].b].y)); if (tang > 0 && by > bxb) { atom[bond[i].b].x = (atom[bond[i].b].x + atom[bond[j].b].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[bond[j].b].y) / 2; atom[bond[j].b].x = (atom[bond[i].b].x + atom[bond[j].b].x) / 2; atom[bond[j].b].y = (atom[bond[i].b].y + atom[bond[j].b].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].b].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].b].min_y); atom[bond[i].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].b].max_x); atom[bond[i].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].b].max_y); atom[bond[j].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].b].min_x); atom[bond[j].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].b].min_y); atom[bond[j].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].b].max_x); atom[bond[j].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].b].max_y); } if (tang < 0 && by > bxa) { atom[bond[i].b].x = (atom[bond[i].b].x + atom[bond[j].a].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[bond[j].a].y) / 2; atom[bond[j].a].x = (atom[bond[i].b].x + atom[bond[j].a].x) / 2; atom[bond[j].a].y = (atom[bond[i].b].y + atom[bond[j].a].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].a].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].a].min_y); atom[bond[i].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].a].max_x); atom[bond[i].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].a].max_y); atom[bond[j].a].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].a].max_y); } } break; } else { bond[j].exists = false; bond[i].type = 1; if (bond[j].arom) bond[i].arom = true; if (l2 > avg / 2) { double ay = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y)); double axa = fabs(distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y)); double axb = fabs(distance_from_bond_x_b(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y)); if (tang > 0 && ay > axa) { atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[j].a].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[j].a].y) / 2; atom[bond[j].a].x = (atom[bond[i].a].x + atom[bond[j].a].x) / 2; atom[bond[j].a].y = (atom[bond[i].a].y + atom[bond[j].a].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].a].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].a].min_y); atom[bond[i].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].a].max_x); atom[bond[i].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].a].max_y); atom[bond[j].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].a].max_y); } if (tang < 0 && ay > axb) { atom[bond[j].a].x = (atom[bond[j].a].x + atom[bond[i].b].x) / 2; atom[bond[j].a].y = (atom[bond[j].a].y + atom[bond[i].b].y) / 2; atom[bond[i].b].x = (atom[bond[j].a].x + atom[bond[i].b].x) / 2; atom[bond[i].b].y = (atom[bond[j].a].y + atom[bond[i].b].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].b].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].b].min_y); atom[bond[i].a].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].b].max_x); atom[bond[i].a].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].b].max_y); atom[bond[j].b].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].b].min_x); atom[bond[j].b].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].b].min_y); atom[bond[j].b].max_x = std::min(atom[bond[i].a].max_x, atom[bond[j].b].max_x); atom[bond[j].b].max_y = std::min(atom[bond[i].a].max_y, atom[bond[j].b].max_y); } double by = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y)); double bxa = fabs(distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y)); double bxb = fabs(distance_from_bond_x_b(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y)); if (tang > 0 && by > bxb) { atom[bond[i].b].x = (atom[bond[i].b].x + atom[bond[j].b].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[bond[j].b].y) / 2; atom[bond[j].b].x = (atom[bond[i].b].x + atom[bond[j].b].x) / 2; atom[bond[j].b].y = (atom[bond[i].b].y + atom[bond[j].b].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].b].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].b].min_y); atom[bond[i].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].b].max_x); atom[bond[i].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].b].max_y); atom[bond[j].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].b].min_x); atom[bond[j].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].b].min_y); atom[bond[j].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].b].max_x); atom[bond[j].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].b].max_y); } if (tang < 0 && by > bxa) { atom[bond[j].b].x = (atom[bond[j].b].x + atom[bond[i].a].x) / 2; atom[bond[j].b].y = (atom[bond[j].b].y + atom[bond[i].a].y) / 2; atom[bond[i].a].x = (atom[bond[j].b].x + atom[bond[i].a].x) / 2; atom[bond[i].a].y = (atom[bond[j].b].y + atom[bond[i].a].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].a].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].a].min_y); atom[bond[i].b].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].a].max_x); atom[bond[i].b].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].a].max_y); atom[bond[j].a].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].a].min_x); atom[bond[j].a].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].a].min_y); atom[bond[j].a].max_x = std::min(atom[bond[i].b].max_x, atom[bond[j].a].max_x); atom[bond[j].a].max_y = std::min(atom[bond[i].b].max_y, atom[bond[j].a].max_y); } } } } } } std::sort(a.begin(), a.end()); if (n > 0) thickness = a[(n - 1) / 2]; else thickness = dist; return (thickness); } double dist_double_bonds(const std::vector &atom, std::vector &bond, int n_bond, double avg) { std::vector a; int n = 0; double max_dist_double_bond = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists) { double l1 = bond_length(bond, i, atom); bond[i].conjoined = false; for (int j = i + 1; j < n_bond; j++) if ((bond[j].exists) && (fabs(angle_between_bonds(bond, i, j, atom)) > D_T_TOLERANCE)) { double l2 = bond_length(bond, j, atom); double dbb = distance_between_bonds(bond, i, j, atom); if (dbb < avg / 2 && l1 > avg / 3 && l2 > avg / 3 && bonds_within_each_other(bond, i, j, atom)) { a.push_back(dbb); n++; } } } std::sort(a.begin(), a.end()); //for (int i = 0; i < n; i++) cout << a[i] << endl; //cout << "-----------------" << endl; if (n > 0) max_dist_double_bond = a[3 * (n - 1) / 4]; if (max_dist_double_bond < 1) max_dist_double_bond = avg / 3; else { max_dist_double_bond += 2; for (int i = 0; i < n; i++) if (a[i] - max_dist_double_bond < 1 && a[i] > max_dist_double_bond) max_dist_double_bond = a[i]; } max_dist_double_bond += 0.001; return (max_dist_double_bond); } int double_triple_bonds(std::vector &atom, std::vector &bond, int n_bond, double avg, int &n_atom, double max_dist_double_bond) { for (int i = 0; i < n_bond; i++) if (bond[i].exists) { double l1 = bond_length(bond, i, atom); for (int j = i + 1; j < n_bond; j++) if ((bond[j].exists) && (fabs(angle_between_bonds(bond, i, j, atom)) > D_T_TOLERANCE)) { double l2 = bond_length(bond, j, atom); double dij = distance_between_bonds(bond, i, j, atom); if (dij <= max_dist_double_bond && bonds_within_each_other(bond, i, j, atom)) { // start triple bond search for (int k = j + 1; k < n_bond; k++) if ((bond[k].exists) && (fabs(angle_between_bonds(bond, k, j, atom)) > D_T_TOLERANCE)) { double l3 = bond_length(bond, k, atom); double djk = distance_between_bonds(bond, k, j, atom); double dik = distance_between_bonds(bond, k, i, atom); if (djk <= max_dist_double_bond && bonds_within_each_other(bond, k, j, atom)) { if (dik > dij) { bond[k].exists = false; if ((l3 > l2 / 2) || (l2 > avg && l2 > 1.5 * l3 && l3 > 0.5 * avg)) { bond[j].type += bond[k].type; if (bond[j].curve == bond[k].curve) bond[j].conjoined = true; } if (bond[k].arom) bond[j].arom = true; } else { bond[j].exists = false; if ((l2 > l3 / 2) || (l3 > avg && l3 > 1.5 * l2 && l2 > 0.5 * avg)) { bond[k].type += bond[j].type; if (bond[j].curve == bond[k].curve) bond[k].conjoined = true; } if (bond[j].arom) bond[k].arom = true; break; } } } if (!bond[j].exists) continue; // end triple bond search int ii = i, jj = j; double l11 = l1, l22 = l2; bool extended_triple = false; if (l1 > avg && l1 > 1.5 * l2 && l2 > 0.5 * avg) extended_triple = true; else if (l2 > avg && l2 > 1.5 * l1 && l1 > 0.5 * avg) { ii = j; jj = i; l11 = l2; l22 = l1; extended_triple = true; } if (extended_triple) { double aa = fabs(distance_from_bond_x_a(atom[bond[ii].a].x, atom[bond[ii].a].y, atom[bond[ii].b].x, atom[bond[ii].b].y, atom[bond[jj].a].x, atom[bond[jj].a].y)); double ab = fabs(distance_from_bond_x_a(atom[bond[ii].a].x, atom[bond[ii].a].y, atom[bond[ii].b].x, atom[bond[ii].b].y, atom[bond[jj].b].x, atom[bond[jj].b].y)); double ba = fabs(distance_from_bond_x_b(atom[bond[ii].a].x, atom[bond[ii].a].y, atom[bond[ii].b].x, atom[bond[ii].b].y, atom[bond[jj].a].x, atom[bond[jj].a].y)); double bb = fabs(distance_from_bond_x_b(atom[bond[ii].a].x, atom[bond[ii].a].y, atom[bond[ii].b].x, atom[bond[ii].b].y, atom[bond[jj].b].x, atom[bond[jj].b].y)); double da = std::min(aa, ab); double db = std::min(ba, bb); if (da > 0.5 * l22) { double x = atom[bond[ii].a].x + (atom[bond[ii].b].x - atom[bond[ii].a].x) * da / l11; double y = atom[bond[ii].a].y + (atom[bond[ii].b].y - atom[bond[ii].a].y) * da / l11; int ba = bond[ii].a; atom_t at1(x,y,bond[ii].curve); at1.exists = true; if (n_atom < MAX_ATOMS) { bond[ii].a = n_atom; atom.push_back(at1); n_atom++; } bond_t b1(ba,bond[ii].a,bond[ii].curve); if (n_bond < MAX_ATOMS) { bond.push_back(b1); n_bond++; } } if (db > 0.5 * l22) { double x = atom[bond[ii].b].x + (atom[bond[ii].a].x - atom[bond[ii].b].x) * db / l11; double y = atom[bond[ii].b].y + (atom[bond[ii].a].y - atom[bond[ii].b].y) * db / l11; int ba = bond[ii].b; atom_t at1(x,y,bond[ii].curve); at1.exists = true; if (n_atom < MAX_ATOMS) { bond[ii].b = n_atom; atom.push_back(at1); n_atom++; } bond_t b1(ba,bond[ii].b,bond[ii].curve); if (n_bond < MAX_ATOMS) { bond.push_back(b1); n_bond++; } } bond[jj].exists = false; bond[ii].type += bond[jj].type; if (bond[jj].arom) bond[ii].arom = true; if (bond[jj].curve == bond[ii].curve) bond[ii].conjoined = true; if (i == jj) break; } else { if (l1 > l2) { bond[j].exists = false; if (l2 > l1 / 2) { bond[i].type += bond[j].type; if (bond[j].curve == bond[i].curve) bond[i].conjoined = true; } if (bond[j].arom) bond[i].arom = true; } else { bond[i].exists = false; if (l1 > l2 / 2) { bond[j].type += bond[i].type; if (bond[j].curve == bond[i].curve) bond[j].conjoined = true; } if (bond[i].arom) bond[j].arom = true; break; } } } } } return (n_bond); } void extend_terminal_bond_to_label( std::vector &atom, const std::vector &letters, int n_letters, const std::vector &bond, int n_bond, const std::vector &label, int n_label, double avg, double maxh, double max_dist_double_bond) { for (int j = 0; j < n_bond; j++) if (bond[j].exists) { bool not_corner_a = terminal_bond(bond[j].a, j, bond, n_bond); bool not_corner_b = terminal_bond(bond[j].b, j, bond, n_bond); if (atom[bond[j].a].label != " ") not_corner_a = false; if (atom[bond[j].b].label != " ") not_corner_b = false; double xa = atom[bond[j].a].x; double ya = atom[bond[j].a].y; double xb = atom[bond[j].b].x; double yb = atom[bond[j].b].y; double bl = bond_length(bond, j, atom); double minb = FLT_MAX; bool found1 = false, found2 = false; int l1 = -1, l2 = -1; if (not_corner_a) { for (int i = 0; i < n_label; i++) if ((label[i].a)[0] != '+' && (label[i].a)[0] != '-') { double d1 = distance_from_bond_x_a(xa, ya, xb, yb, label[i].x1, label[i].y1); double d2 = distance_from_bond_x_a(xa, ya, xb, yb, label[i].x2, label[i].y2); double h1 = fabs(distance_from_bond_y(xa, ya, xb, yb, label[i].x1, label[i].y1)); double h2 = fabs(distance_from_bond_y(xa, ya, xb, yb, label[i].x2, label[i].y2)); double y_dist = maxh + label[i].r1 / 2; if (bond[j].type > 1) y_dist += max_dist_double_bond; double nb = fabs(d1) - label[i].r1 / 2; if (nb <= avg && h1 <= y_dist && nb < minb && d1 < bl / 2) { found1 = true; l1 = i; minb = nb; } y_dist = maxh + label[i].r2 / 2; if (bond[j].type > 1) y_dist += max_dist_double_bond; nb = fabs(d2) - label[i].r2 / 2; if (nb <= avg && h2 <= y_dist && nb < minb && d2 < bl / 2) { found1 = true; l1 = i; minb = nb; } } for (int i = 0; i < n_letters; i++) if (letters[i].free && letters[i].a != '+' && letters[i].a != '-') { double d = distance_from_bond_x_a(xa, ya, xb, yb, letters[i].x, letters[i].y); double y_dist = maxh + letters[i].r / 2; if (bond[j].type > 1) y_dist += max_dist_double_bond; double h = fabs(distance_from_bond_y(xa, ya, xb, yb, letters[i].x, letters[i].y)); double nb = fabs(d) - letters[i].r / 2; if (nb <= avg && h <= y_dist && nb < minb && d < bl / 2) { found2 = true; l2 = i; minb = nb; } } if (found2) { //cout<< letters[l2].a<<" "< 1) y_dist += max_dist_double_bond; double nb = fabs(d1) - label[i].r1 / 2; // end "b" and 1st side if (nb <= avg && h1 <= y_dist && nb < minb && d1 > -bl / 2) { found1 = true; l1 = i; minb = nb; } y_dist = maxh + label[i].r2 / 2; if (bond[j].type > 1) y_dist += max_dist_double_bond; nb = fabs(d2) - label[i].r2 / 2; // end "b" and 2nd side if (nb <= avg && h2 <= y_dist && nb < minb && d2 > -bl / 2) { found1 = true; l1 = i; minb = nb; } } for (int i = 0; i < n_letters; i++) if (letters[i].free && letters[i].a != '+' && letters[i].a != '-' && i != l2) { double d = distance_from_bond_x_b(xa, ya, xb, yb, letters[i].x, letters[i].y); double nb = fabs(d) - letters[i].r / 2; // distance between end "b" and letter double y_dist = maxh + letters[i].r / 2; if (bond[j].type > 1) y_dist += max_dist_double_bond; double h = fabs(distance_from_bond_y(xa, ya, xb, yb, letters[i].x, letters[i].y)); if (nb <= avg && h <= y_dist && nb < minb && d > -bl / 2) { found2 = true; l2 = i; minb = nb; } } if (found2) { //cout< &atom, std::vector &bond, int n_bond, double avg, double maxh, double max_dist_double_bond) { bool found_intersection = true; while (found_intersection) { found_intersection = false; for (int j = 0; j < n_bond; j++) if (bond[j].exists) { bool not_corner_a = terminal_bond(bond[j].a, j, bond, n_bond); bool not_corner_b = terminal_bond(bond[j].b, j, bond, n_bond); double xa = atom[bond[j].a].x; double ya = atom[bond[j].a].y; double xb = atom[bond[j].b].x; double yb = atom[bond[j].b].y; double bl = bond_length(bond, j, atom); double minb = FLT_MAX; bool found = false; int l = -1; for (int i = 0; i < n_bond; i++) if (bond[i].exists && i != j) if (not_corner_a) { double h1 = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, xa, ya)); double y_dist = maxh; double y_dist1 = maxh; if (bond[j].type > 1 && !bond[j].conjoined) y_dist += max_dist_double_bond; if (bond[i].type > 1 && !bond[i].conjoined) y_dist1 += max_dist_double_bond; int ai = bond[i].a; if (ai != bond[j].a && ai != bond[j].b) { double d = distance_from_bond_x_a(xa, ya, xb, yb, atom[ai].x, atom[ai].y); double h = fabs(distance_from_bond_y(xa, ya, xb, yb, atom[ai].x, atom[ai].y)); if (fabs(d) <= avg / 2 && h <= y_dist && fabs(d) < minb && d < bl / 2 && h1 < y_dist1) { found = true; l = ai; minb = fabs(d); } } int bi = bond[i].b; if (bi != bond[j].a && bi != bond[j].b) { double d = distance_from_bond_x_a(xa, ya, xb, yb, atom[bi].x, atom[bi].y); double h = fabs(distance_from_bond_y(xa, ya, xb, yb, atom[bi].x, atom[bi].y)); if (fabs(d) <= avg / 2 && h <= y_dist && fabs(d) < minb && d < bl / 2 && h1 < y_dist1) { found = true; l = bi; minb = fabs(d); } } } if (found) { if (atom[l].label.length() < 2 && atom[bond[j].a].label.length() < 2) { atom[l].x = (atom[bond[j].a].x + atom[l].x) / 2; atom[l].y = (atom[bond[j].a].y + atom[l].y) / 2; atom[l].min_x = std::min(atom[bond[j].a].min_x ,atom[l].min_x); atom[l].min_y = std::min(atom[bond[j].a].min_y ,atom[l].min_y); atom[l].max_x = std::max(atom[bond[j].a].max_x ,atom[l].max_x); atom[l].max_y = std::max(atom[bond[j].a].max_y ,atom[l].max_y); bond[j].a = l; found_intersection = true; } //cout< 1 && !bond[j].conjoined) y_dist += max_dist_double_bond; if (bond[i].type > 1 && !bond[i].conjoined) y_dist1 += max_dist_double_bond; int ai = bond[i].a; if (ai != bond[j].a && ai != bond[j].b) { double d = distance_from_bond_x_b(xa, ya, xb, yb, atom[ai].x, atom[ai].y); double h = fabs(distance_from_bond_y(xa, ya, xb, yb, atom[ai].x, atom[ai].y)); if (fabs(d) <= avg / 2 && h <= y_dist && fabs(d) < minb && d > -bl / 2 && h1 < y_dist1) { found = true; l = ai; minb = fabs(d); } } int bi = bond[i].b; if (bi != bond[j].a && bi != bond[j].b) { double d = distance_from_bond_x_b(xa, ya, xb, yb, atom[bi].x, atom[bi].y); double h = fabs(distance_from_bond_y(xa, ya, xb, yb, atom[bi].x, atom[bi].y)); if (fabs(d) <= avg / 2 && h <= y_dist && fabs(d) < minb && d > -bl / 2 && h1 < y_dist1) { found = true; l = bi; minb = fabs(d); } } } if (found) { if (atom[l].label.length() < 2 && atom[bond[j].b].label.length() < 2) { atom[l].x = (atom[bond[j].b].x + atom[l].x) / 2; atom[l].y = (atom[bond[j].b].y + atom[l].y) / 2; atom[l].min_x = std::min(atom[bond[j].b].min_x ,atom[l].min_x); atom[l].min_y = std::min(atom[bond[j].b].min_y ,atom[l].min_y); atom[l].max_x = std::max(atom[bond[j].b].max_x ,atom[l].max_x); atom[l].max_y = std::max(atom[bond[j].b].max_y ,atom[l].max_y); bond[j].b = l; found_intersection = true; } //cout< &atom, std::vector &bond, int n_atom, int n_bond, const std::map &fix, const std::map &superatom, bool debug) { for (int j = 0; j < n_bond; j++) if (bond[j].exists && (!atom[bond[j].a].exists || !atom[bond[j].b].exists)) bond[j].exists = false; for (int i = 0; i < n_atom; i++) if (atom[i].exists) { int n = 0; int m = 0; for (int j = 0; j < n_bond; j++) if (bond[j].exists && (bond[j].a == i || bond[j].b == i)) { n += bond[j].type; if (bond[j].type > 1) m++; } atom[i].charge = 0; bool cont = true; while (cont) { std::string::size_type pos = atom[i].label.find_first_of('-'); if (pos != std::string::npos) { atom[i].label.erase(pos, 1); if (atom[i].label.length() > 0 && isalpha(atom[i].label.at(0))) atom[i].charge--; } else { pos = atom[i].label.find_first_of('+'); if (pos != std::string::npos) { atom[i].label.erase(pos, 1); if (atom[i].label.length() > 0 && isalpha(atom[i].label.at(0))) atom[i].charge++; } else cont = false; } } for (int j = 0; j < n_bond; j++) if (bond[j].exists && bond[j].hash && (bond[j].b == i || bond[j].a == i)) atom[i].charge = 0; atom[i].label = fix_atom_name(atom[i].label, n, fix, superatom, debug); } } int next_atom(int cur, int begin, int total) { int n = cur + 1; if (n > total - 1) { n = begin; } return (n); } bool dir_change(int n, int last, int begin, int total, const std::vector &atom) { int m = next_atom(n, begin, total); while (distance(atom[m].x, atom[m].y, atom[n].x, atom[n].y) < V_DISPLACEMENT && m != n) m = next_atom(m, begin, total); if (m == n) return (false); double s = fabs(distance_from_bond_y(atom[n].x, atom[n].y, atom[last].x, atom[last].y, atom[m].x, atom[m].y)); if (s > DIR_CHANGE) return (true); return (false); } bool smaller_distance(int n, int last, int begin, int total, const std::vector &atom) { int m = next_atom(n, begin, total); double d1 = distance(atom[n].x, atom[n].y, atom[last].x, atom[last].y); double d2 = distance(atom[m].x, atom[m].y, atom[last].x, atom[last].y); if (d1 > d2) { return (true); } return (false); } void douglas_peucker(std::vector &atom, int b_atom, int n_atom) { double max_s = 0; double max_i = b_atom; for (int i=b_atom+1; imax_s) { max_i = i; max_s = s; } } if (max_s>DIR_CHANGE) { std::vector atom1(atom); std::vector atom2(atom); douglas_peucker(atom1, b_atom, max_i); douglas_peucker(atom2, max_i, n_atom); for (int i=b_atom; i &atom, std::vector &bond, int b_atom, int n_atom, int n_bond, const potrace_path_t * const p) { //douglas_peucker(atom, b_atom, n_atom); int i = b_atom + 1; int last = b_atom; while (i < n_atom) { if (atom[i].corner) { atom[i].exists = true; last = i; i++; } else if (dir_change(i, last, b_atom, n_atom, atom)) { atom[i].exists = true; last = i; i++; } else if (smaller_distance(i, last, b_atom, n_atom, atom)) { atom[i].exists = true; last = i; i++; } else { i++; } } for (int i = b_atom; i < n_atom; i++) if (atom[i].exists) { int j = next_atom(i, b_atom, n_atom); while (!atom[j].exists) { j = next_atom(j, b_atom, n_atom); } bond_t bn(i,j,p); if (n_bond < MAX_ATOMS) { bond.push_back(bn); n_bond++; } } return (n_bond); } int find_atoms(const potrace_path_t *p, std::vector &atom, std::vector &bond, int *n_bond,int width, int height) { int *tag, n_atom = 0; potrace_dpoint_t (*c)[3]; long n; while (p != NULL) { n = p->curve.n; tag = p->curve.tag; c = p->curve.c; int b_atom = n_atom; double x = c[n - 1][2].x; double y = c[n - 1][2].y; if (x<0) x=0; if (x>width) x=width; if (y<0) y=0; if (y>height) y=height; atom_t at(x,y,p); if (n_atom < MAX_ATOMS) { atom.push_back(at); n_atom++; } for (long i = 0; i < n; i++) { switch (tag[i]) { case POTRACE_CORNER: { x = c[i][1].x; y = c[i][1].y; if (x<0) x=0; if (x>width) x=width; if (y<0) y=0; if (y>height) y=height; atom_t at1(x,y,p); at1.corner = true; if (n_atom < MAX_ATOMS) { atom.push_back(at1); n_atom++; } } break; case POTRACE_CURVETO: { x = c[i][0].x; y = c[i][0].y; if (x<0) x=0; if (x>width) x=width; if (y<0) y=0; if (y>height) y=height; atom_t at2(x,y,p); if (n_atom < MAX_ATOMS) { atom.push_back(at2); n_atom++; } x = c[i][1].x; y = c[i][1].y; if (x<0) x=0; if (x>width) x=width; if (y<0) y=0; if (y>height) y=height; atom_t at3(x,y,p); if (n_atom < MAX_ATOMS) { atom.push_back(at3); n_atom++; } } break; } if (i != n - 1) { x = c[i][2].x; y = c[i][2].y; if (x<0) x=0; if (x>width) x=width; if (y<0) y=0; if (y>height) y=height; atom_t at4(x,y,p); if (n_atom < MAX_ATOMS) { atom.push_back(at4); n_atom++; } } } *n_bond = find_bonds(atom, bond, b_atom, n_atom, *n_bond, p); p = p->next; } return (n_atom); } bool comp_dashes_x(const dash_t &a, const dash_t &b) { return (a.x < b.x); } bool comp_dashes_y(const dash_t &a, const dash_t &b) { return (a.y < b.y); } void extend_dashed_bond(int a, int b, int n, std::vector &atom) { double x0 = atom[a].x; double y0 = atom[a].y; double x1 = atom[b].x; double y1 = atom[b].y; double l = distance(x0, y0, x1, y1); double kx = (x1 - x0) / l; double ky = (y1 - y0) / l; double x = kx * (-1. * l / (n - 1)) + x0; double y = ky * (-1. * l / (n - 1)) + y0; atom[a].x = x; atom[a].y = y; atom[b].x = kx * l / (n - 1) + x1; atom[b].y = ky * l / (n - 1) + y1; } int count_area(std::vector > &box, double &x0, double &y0) { int a = 0; int w = box.size(); int h = box[0].size(); int x = int(x0); int y = int(y0); int xm = 0, ym = 0; if (box[x][y] == 1) { box[x][y] = 2; std::list cx; std::list cy; cx.push_back(x); cy.push_back(y); while (!cx.empty()) { x = cx.front(); y = cy.front(); cx.pop_front(); cy.pop_front(); box[x][y] = 0; a++; xm += x; ym += y; for (int i = x - 1; i < x + 2; i++) for (int j = y - 1; j < y + 2; j++) if (i < w && j < h && i >= 0 && j >= 0 && box[i][j] == 1) { cx.push_back(i); cy.push_back(j); box[i][j] = 2; } } } else return (0); x0 = 1. * xm / a; y0 = 1. * ym / a; return (a); } bool curve_in_letter(const potrace_path_t *p, std::vector &letters) { bool r = false; for (int i=0; i &atom, std::vector &bond, int n_atom, int *n_bond, int max, double max_dash_length, const Image &img, const ColorGray &bg, double THRESHOLD, bool thick, double dist, std::vector &letters) { potrace_dpoint_t (*c)[3]; std::vector dot; std::vector > box(img.columns()); int width = img.columns(); int height = img.rows(); for (unsigned int i = 0; i < img.columns(); i++) for (unsigned int j = 0; j < img.rows(); j++) box[i].push_back(get_pixel(img, bg, i, j, THRESHOLD)); while (p != NULL) { if (p->sign == int('+') && p->area < max && p->childlist == NULL && !curve_in_letter(p,letters) && dot.size() < 100) { int n = p->curve.n; c = p->curve.c; int *tag = p->curve.tag; double cx = c[n - 1][2].x; double cy = c[n - 1][2].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; dash_t d; d.x = cx; d.y = cy; double l = cx; double r = cx; double t = cy; double b = cy; d.curve = p; d.free = true; int tot = 1; for (long i = 0; i < n; i++) { switch (tag[i]) { case POTRACE_CORNER: cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; d.x += cx; d.y += cy; if (cx < l) l = cx; if (cx > r) r = cx; if (cy < t) t = cy; if (cx > b) b = cy; tot++; break; case POTRACE_CURVETO: cx = c[i][0].x; cy = c[i][0].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; d.x += cx; d.y += cy; if (cx < l) l = cx; if (cx > r) r = cx; if (cy < t) t = cy; if (cx > b) b = cy; cx = c[i][1].x; cy = c[i][1].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; d.x += cx; d.y += cy; if (cx < l) l = cx; if (cx > r) r = cx; if (cy < t) t = cy; if (cx > b) b = cy; tot += 2; break; } if (i != n - 1) { cx = c[i][2].x; cy = c[i][2].y; if (cx<0) cx=0; if (cx>width) cx=width; if (cy<0) cy=0; if (cy>height) cy=height; d.x += cx; d.y += cy; if (cx < l) l = cx; if (cx > r) r = cx; if (cy < t) t = cy; if (cx > b) b = cy; tot++; } } d.x /= tot; d.y /= tot; if (thick) d.area = count_area(box, d.x, d.y); else d.area = p->area; if (distance(l, t, r, b) < max_dash_length) dot.push_back(d); } p = p->next; } for (int i = 0; i < dot.size(); i++) if (dot[i].free) { std::vector dash; dash.push_back(dot[i]); dot[i].free = false; double l = dot[i].x; double r = dot[i].x; double t = dot[i].y; double b = dot[i].y; double mx = l; double my = t; double dist_next = FLT_MAX; int next_dot = i; for (int j = i + 1; j < dot.size(); j++) if (dot[j].free && distance(dash[0].x, dash[0].y, dot[j].x, dot[j].y) <= dist && distance(dash[0].x, dash[0].y, dot[j].x, dot[j].y) < dist_next) { dash.push_back(dot[j]); dist_next = distance(dash[0].x, dash[0].y, dot[j].x, dot[j].y); next_dot = j; } if (next_dot != i) { dot[next_dot].free = false; if (dash[1].x < l) l = dash[1].x; if (dash[1].x > r) r = dash[1].x; if (dash[1].y < t) t = dash[1].y; if (dash[1].y > b) b = dash[1].y; mx = (mx + dash[1].x) / 2; my = (my + dash[1].y) / 2; } bool found = true; while (dash.size() > 1 && found) { dist_next = FLT_MAX; found = false; int minj = next_dot; dash_t d; for (int j = next_dot + 1; j < dot.size(); j++) if (dot[j].free && distance(mx, my, dot[j].x, dot[j].y) <= dist && distance(mx, my, dot[j].x, dot[j].y) < dist_next && fabs(distance_from_bond_y(dash[0].x, dash[0].y, dash.back().x, dash.back().y, dot[j].x, dot[j].y)) < V_DISPLACEMENT) { d = dot[j]; dist_next = distance(mx, my, dot[j].x, dot[j].y); found = true; minj = j; } if (found) { dot[minj].free = false; if (d.x < l) l = d.x; if (d.x > r) r = d.x; if (d.y < t) t = d.y; if (d.y > b) b = d.y; mx = (mx + d.x) / 2; my = (my + d.y) / 2; dash.push_back(d); } } if (dash.size() > 2) { if ((r - l) > (b - t)) { sort(dash.begin(),dash.end(),comp_dashes_x); } else { sort(dash.begin(),dash.end(), comp_dashes_y); } bool one_line = true; double dx = dash.back().x - dash[0].x; double dy = dash.back().y - dash[0].y; double k = 0; if (fabs(dx) > fabs(dy)) k = dy / dx; else k = dx / dy; for (int j = 1; j < dash.size() - 1; j++) { double nx = dash[j].x - dash[0].x; double ny = dash[j].y - dash[0].y; double diff = 0; if (fabs(dx) > fabs(dy)) diff = k * nx - ny; else diff = k * ny - nx; if (fabs(diff) > V_DISPLACEMENT || dash[j].curve == dash[0].curve) one_line = false; } if (one_line) { for (int j = 0; j < dash.size(); j++) delete_curve(atom, bond, n_atom, *n_bond, dash[j].curve); atom_t at1(dash[0].x,dash[0].y,dash[0].curve); at1.exists = true; if (n_atom < MAX_ATOMS) { atom.push_back(at1); n_atom++; } atom_t at2(dash.back().x,dash.back().y,dash.back().curve); at2.exists = true; if (n_atom < MAX_ATOMS) { atom.push_back(at2); n_atom++; } bond_t b1(n_atom-2,n_atom-1,dash[0].curve); b1.hash = true; if (*n_bond < MAX_ATOMS) { bond.push_back(b1); if (dash[0].area > dash.back().area) bond_end_swap(bond, *n_bond); extend_dashed_bond(bond[*n_bond].a, bond[*n_bond].b, dash.size(), atom); (*n_bond)++; } } } } return (n_atom); } int find_small_bonds(const potrace_path_t *p, std::vector &atom, std::vector &bond, int n_atom, int *n_bond, double max_area, double Small, double thickness) { while (p != NULL) { if ((p->sign == int('+')) && (p->area <= max_area)) { std::vector dot; for (int i = 0; i < n_atom; i++) if ((atom[i].exists) && (atom[i].curve == p) && (dot.size() < 20)) { dash_t d; d.x = atom[i].x; d.y = atom[i].y; d.curve = p; d.free = true; dot.push_back(d); } if ((dot.size() > 2)) { double l = dot[0].x; double r = dot[0].x; double t = dot[0].y; double b = dot[0].y; for (int i = 1; i < dot.size(); i++) { if (dot[i].x < l) l = dot[i].x; if (dot[i].x > r) r = dot[i].x; if (dot[i].y < t) t = dot[i].y; if (dot[i].y > b) b = dot[i].y; } if ((r - l) > (b - t)) { sort(dot.begin(),dot.end(),comp_dashes_x); } else { sort(dot.begin(),dot.end(), comp_dashes_y); } double d = 0; for (int i = 1; i < dot.size() - 1; i++) d = std::max(d, fabs(distance_from_bond_y(dot[0].x, dot[0].y, dot.back().x, dot.back().y, dot[i].x, dot[i].y))); if (d < thickness || p->area < Small) { delete_curve(atom, bond, n_atom, *n_bond, p); atom_t at1(dot[0].x,dot[0].y,p); at1.exists = true; if (n_atom < MAX_ATOMS) { atom.push_back(at1); n_atom++; } atom_t at2(dot.back().x,dot.back().y,p); at2.exists = true; if (n_atom < MAX_ATOMS) { atom.push_back(at2); n_atom++; } bond_t b1(n_atom-2,n_atom-1,p); b1.Small = true; if (*n_bond < MAX_ATOMS) { bond.push_back(b1); (*n_bond)++; } } } } p = p->next; } return (n_atom); } int resolve_bridge_bonds(std::vector &atom, int n_atom, std::vector &bond, int n_bond, double thickness, double avg_bond_length, const std::map &superatom, bool verbose) { molecule_statistics_t molecule_statistics1 = calculate_molecule_statistics(atom, bond, n_bond, avg_bond_length, superatom, verbose); for (int i = 0; i < n_atom; i++) if ((atom[i].exists) && (atom[i].label == " ")) { std::list con; for (int j = 0; j < n_bond; j++) if ((bond[j].exists) && (bond[j].a == i || bond[j].b == i)) con.push_back(j); if (con.size() == 4) { int a = con.front(); con.pop_front(); int b = 0; int e = 0; while ((con.size() > 2) && (e++ < 3)) { b = con.front(); con.pop_front(); double y1 = distance_from_bond_y(atom[bond[a].a].x, atom[bond[a].a].y, atom[bond[a].b].x, atom[bond[a].b].y, atom[bond[b].a].x, atom[bond[b].a].y); double y2 = distance_from_bond_y(atom[bond[a].a].x, atom[bond[a].a].y, atom[bond[a].b].x, atom[bond[a].b].y, atom[bond[b].b].x, atom[bond[b].b].y); if (fabs(y1) > thickness || fabs(y2) > thickness) con.push_back(b); } if (con.size() == 2) { int c = con.front(); con.pop_front(); int d = con.front(); con.pop_front(); std::vector term; term.push_back(a); term.push_back(b); term.push_back(c); term.push_back(d); bool terminal = false; for (unsigned int k = 0; k < term.size(); k++) { bool terminal_a = terminal_bond(bond[term[k]].a, term[k], bond, n_bond); bool terminal_b = terminal_bond(bond[term[k]].b, term[k], bond, n_bond); if (terminal_a || terminal_b) terminal = true; } double y1 = distance_from_bond_y(atom[bond[c].a].x, atom[bond[c].a].y, atom[bond[c].b].x, atom[bond[c].b].y, atom[bond[d].a].x, atom[bond[d].a].y); double y2 = distance_from_bond_y(atom[bond[c].a].x, atom[bond[c].a].y, atom[bond[c].b].x, atom[bond[c].b].y, atom[bond[d].b].x, atom[bond[d].b].y); if (bond[a].type == 1 && bond[b].type == 1 && bond[c].type == 1 && bond[d].type == 1 && fabs(y1) < thickness && fabs(y2) < thickness && !terminal) { bond[b].exists = false; bond[d].exists = false; atom[i].exists = false; if (bond[a].a == bond[b].a) bond[a].a = bond[b].b; else if (bond[a].a == bond[b].b) bond[a].a = bond[b].a; else if (bond[a].b == bond[b].a) bond[a].b = bond[b].b; else if (bond[a].b == bond[b].b) bond[a].b = bond[b].a; if (bond[c].a == bond[d].a) bond[c].a = bond[d].b; else if (bond[c].a == bond[d].b) bond[c].a = bond[d].a; else if (bond[c].b == bond[d].a) bond[c].b = bond[d].b; else if (bond[c].b == bond[d].b) bond[c].b = bond[d].a; molecule_statistics_t molecule_statistics2 = calculate_molecule_statistics(atom, bond, n_bond, avg_bond_length, superatom, verbose); if (molecule_statistics1.fragments != molecule_statistics2.fragments || molecule_statistics1.rotors != molecule_statistics2.rotors || molecule_statistics1.rings456 - molecule_statistics2.rings456 == 2) { bond[b].exists = true; bond[d].exists = true; atom[i].exists = true; if (bond[a].a == bond[b].a) bond[a].a = bond[b].b; else if (bond[a].a == bond[b].b) bond[a].a = bond[b].a; else if (bond[a].b == bond[b].a) bond[a].b = bond[b].b; else if (bond[a].b == bond[b].b) bond[a].b = bond[b].a; if (bond[c].a == bond[d].a) bond[c].a = bond[d].b; else if (bond[c].a == bond[d].b) bond[c].a = bond[d].a; else if (bond[c].b == bond[d].a) bond[c].b = bond[d].b; else if (bond[c].b == bond[d].b) bond[c].b = bond[d].a; } } } } } return (molecule_statistics1.fragments); } void collapse_atoms(std::vector &atom, std::vector &bond, int n_atom, int n_bond, double dist) { bool found = true; while (found) { found = false; for (int i = 0; i < n_atom; i++) if (atom[i].exists) for (int j = 0; j < n_atom; j++) if (atom[j].exists && j != i && distance(atom[i].x, atom[i].y, atom[j].x, atom[j].y) < dist) { atom[j].exists = false; atom[i].x = (atom[i].x + atom[j].x) / 2; atom[i].y = (atom[i].y + atom[j].y) / 2; atom[i].min_x = std::min(atom[i].min_x, atom[j].min_x); atom[i].min_y = std::min(atom[i].min_y, atom[j].min_y); atom[i].max_x = std::max(atom[i].max_x, atom[j].max_x); atom[i].max_y = std::max(atom[i].max_y, atom[j].max_y); if (atom[j].label != " " && atom[i].label == " ") atom[i].label = atom[j].label; for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == j) { bond[k].a = i; } else if (bond[k].b == j) { bond[k].b = i; } } found = true; } } } void collapse_bonds(std::vector &atom, const std::vector &bond, int n_bond, double dist) { for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond_length(bond, i, atom) < dist) { atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[i].b].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[i].b].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[i].b].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[i].b].min_y); atom[bond[i].a].max_x = std::max(atom[bond[i].a].max_x, atom[bond[i].b].max_x); atom[bond[i].a].max_y = std::max(atom[bond[i].a].max_y, atom[bond[i].b].max_y); atom[bond[i].b].x = (atom[bond[i].a].x + atom[bond[i].b].x) / 2; atom[bond[i].b].y = (atom[bond[i].a].y + atom[bond[i].b].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].a].min_x, atom[bond[i].b].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].a].min_y, atom[bond[i].b].min_y); atom[bond[i].b].max_x = std::max(atom[bond[i].a].max_x, atom[bond[i].b].max_x); atom[bond[i].b].max_y = std::max(atom[bond[i].a].max_y, atom[bond[i].b].max_y); } } int fix_one_sided_bonds(std::vector &bond, int n_bond, const std::vector &atom, double thickness, double avg) { double l; int n_bond_orig = n_bond; for (int i = 0; i < n_bond_orig; i++) if (bond[i].exists && bond[i].type < 3 && (l = bond_length(bond, i, atom)) > avg / 3) for (int j = 0; j < n_bond_orig; j++) if (bond[j].exists && j != i && bond[j].type < 3 && fabs(angle_between_bonds(bond, i, j, atom)) < D_T_TOLERANCE && bond_length(bond, j, atom) > avg / 3) { double d1 = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y)); double d2 = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y)); if (d1 < thickness && !(bond[j].a == bond[i].b || bond[j].a == bond[i].a)) { double l1 = distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y); if (l1 > 0 && l1 < l) { if (bond[j].b == bond[i].b || bond[j].b == bond[i].a) { bond[j].exists = false; } else { bond_t b1(bond[j].a,bond[i].b,bond[i].curve); b1.type = bond[i].type; b1.hash = bond[i].hash; b1.wedge = bond[i].wedge; b1.arom = bond[i].arom; b1.conjoined = bond[i].conjoined; if (n_bond < MAX_ATOMS) { bond.push_back(b1); n_bond++; } bond[i].b = bond[j].a; bond[i].wedge = false; } } } else if (d2 < thickness && !(bond[j].b == bond[i].b || bond[j].b == bond[i].a)) { double l1 = distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y); if (l1 > 0 && l1 < l) { if (bond[j].a == bond[i].b || bond[j].a == bond[i].a) { bond[j].exists = false; } else { bond_t b1(bond[j].b,bond[i].b,bond[i].curve); b1.type = bond[i].type; b1.hash = bond[i].hash; b1.wedge = bond[i].wedge; b1.arom = bond[i].arom; b1.conjoined = bond[i].conjoined; if (n_bond < MAX_ATOMS) { bond.push_back(b1); n_bond++; } bond[i].b = bond[j].b; bond[i].wedge = false; } } } } return (n_bond); } int thickness_hor(const Image &image, int x1, int y1, const ColorGray &bgColor, double THRESHOLD_BOND) { int i = 0, s = 0, w = 0; int width = image.columns(); s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); if (s == 0 && x1 + 1 < width) { x1++; s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); } if (s == 0 && x1 - 2 >= 0) { x1 -= 2; s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); } if (s == 1) { while (x1 + i < width && s == 1) s = get_pixel(image, bgColor, x1 + i++, y1, THRESHOLD_BOND); w = i - 1; i = 1; s = 1; while (x1 - i >= 0 && s == 1) s = get_pixel(image, bgColor, x1 - i++, y1, THRESHOLD_BOND); w += i - 1; } return (w); } int thickness_ver(const Image &image, int x1, int y1, const ColorGray &bgColor, double THRESHOLD_BOND) { int i = 0, s = 0, w = 0; int height = image.rows(); s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); if (s == 0 && y1 + 1 < height) { y1++; s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); } if (s == 0 && y1 - 2 >= 0) { y1 -= 2; s = get_pixel(image, bgColor, x1, y1, THRESHOLD_BOND); } if (s == 1) { while (y1 + i < height && s == 1) s = get_pixel(image, bgColor, x1, y1 + i++, THRESHOLD_BOND); w = i - 1; i = 1; s = 1; while (y1 - i >= 0 && s == 1) s = get_pixel(image, bgColor, x1, y1 - i++, THRESHOLD_BOND); w += i - 1; } return (w); } bool comp_thickness(const std::pair &a, const std::pair &b) { return a.second < b.second; } double find_wedge_bonds(const Image &image, std::vector &atom, int n_atom, std::vector &bond, int n_bond, const ColorGray &bgColor, double THRESHOLD_BOND, double max_dist_double_bond, double avg, int limit, int dist) { double l; std::vector > a; int n = 0; a.push_back(std::make_pair(-1, 1.5)); std::vector x_reg, y_reg; bool triangle = false; for (int i = 0; i < n_bond; i++) if (bond[i].exists && !bond[i].hash && bond[i].type == 1 && (l = bond_length(bond, i, atom)) > max_dist_double_bond) { x_reg.clear(); y_reg.clear(); double avg_x = 0, avg_y = 0; int x1 = int((atom[bond[i].a].x + atom[bond[i].b].x) / 2); int y1 = int((atom[bond[i].a].y + atom[bond[i].b].y) / 2); int w = 0, max_c, min_c, sign = 1; int w3_ver = thickness_ver(image, x1, y1, bgColor, THRESHOLD_BOND); int w3_hor = thickness_hor(image, x1, y1, bgColor, THRESHOLD_BOND); if (w3_ver == 0 && w3_hor == 0) continue; if ((w3_ver < w3_hor && w3_ver > 0) || w3_hor == 0) { w = w3_ver; int old = w3_ver; max_c = int(std::max(atom[bond[i].a].x, atom[bond[i].b].x)) - dist; min_c = int(std::min(atom[bond[i].a].x, atom[bond[i].b].x)) + dist; if (atom[bond[i].b].x < atom[bond[i].a].x) sign = -1; for (int j = x1 + 1; j <= max_c; j++) { int y = int(atom[bond[i].a].y + (atom[bond[i].b].y - atom[bond[i].a].y) * (j - atom[bond[i].a].x) / (atom[bond[i].b].x - atom[bond[i].a].x)); int t = thickness_ver(image, j, y, bgColor, THRESHOLD_BOND); if (abs(t - old) > 2) break; if (t < 2 * MAX_BOND_THICKNESS && t < avg / 3 && t > 0) { x_reg.push_back(j); y_reg.push_back(t); avg_x += j; avg_y += t; w = std::max(w, t); } old = t; } old = w3_ver; for (int j = x1 - 1; j >= min_c; j--) { int y = int(atom[bond[i].a].y + (atom[bond[i].b].y - atom[bond[i].a].y) * (j - atom[bond[i].a].x) / (atom[bond[i].b].x - atom[bond[i].a].x)); int t = thickness_ver(image, j, y, bgColor, THRESHOLD_BOND); if (abs(t - old) > 2) break; if (t < 2 * MAX_BOND_THICKNESS && t < avg / 3 && t > 0) { x_reg.push_back(j); y_reg.push_back(t); avg_x += j; avg_y += t; w = std::max(w, t); } old = t; } } else { w = w3_hor; int old = w3_hor; max_c = int(std::max(atom[bond[i].a].y, atom[bond[i].b].y)) - dist; min_c = int(std::min(atom[bond[i].a].y, atom[bond[i].b].y)) + dist; if (atom[bond[i].b].y < atom[bond[i].a].y) sign = -1; for (int j = y1 + 1; j <= max_c; j++) { int x = int(atom[bond[i].a].x + (atom[bond[i].b].x - atom[bond[i].a].x) * (j - atom[bond[i].a].y) / (atom[bond[i].b].y - atom[bond[i].a].y)); int t = thickness_hor(image, x, j, bgColor, THRESHOLD_BOND); if (abs(t - old) > 2) break; if (t < 2 * MAX_BOND_THICKNESS && t < avg / 3 && t > 0) { x_reg.push_back(j); y_reg.push_back(t); avg_x += j; avg_y += t; w = std::max(w, t); } old = t; } old = w3_hor; for (int j = y1 - 1; j >= min_c; j--) { int x = int(atom[bond[i].a].x + (atom[bond[i].b].x - atom[bond[i].a].x) * (j - atom[bond[i].a].y) / (atom[bond[i].b].y - atom[bond[i].a].y)); int t = thickness_hor(image, x, j, bgColor, THRESHOLD_BOND); if (abs(t - old) > 2) break; if (t < 2 * MAX_BOND_THICKNESS && t < avg / 3 && t > 0) { x_reg.push_back(j); y_reg.push_back(t); avg_x += j; avg_y += t; w = std::max(w, t); } old = t; } } avg_x /= x_reg.size(); avg_y /= y_reg.size(); double numerator = 0, denominator = 0; for (unsigned int j = 0; j < x_reg.size(); j++) { numerator += 1. * (x_reg[j] - avg_x) * (y_reg[j] - avg_y); denominator += 1. * (x_reg[j] - avg_x) * (x_reg[j] - avg_x); } double beta = 0; if (denominator != 0) beta = numerator / denominator; //cout << fabs(beta) * (max_c - min_c) << " " << (max_c - min_c) << " " << avg << endl; if (fabs(beta) * (max_c - min_c) > limit) { bond[i].wedge = true; triangle = true; if (beta * sign < 0) bond_end_swap(bond, i); } if (bond[i].wedge) { for (int j = 0; j < n_atom; j++) if (atom[j].exists && j != bond[i].b && distance(atom[bond[i].b].x, atom[bond[i].b].y, atom[j].x, atom[j].y) <= w) { atom[j].exists = false; atom[bond[i].b].x = (atom[bond[i].b].x + atom[j].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[j].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[j].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y,atom[j].min_y); atom[bond[i].b].max_x = std::max(atom[bond[i].b].max_x, atom[j].max_x); atom[bond[i].b].max_y = std::max(atom[bond[i].b].max_y,atom[j].max_y); for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == j) { bond[k].a = bond[i].b; } else if (bond[k].b == j) { bond[k].b = bond[i].b; } } } } if (!bond[i].wedge) { a.push_back(std::make_pair(i, int(avg_y))); n++; } } std::sort(a.begin(), a.end(),comp_thickness); double t; if (n > 0) t = a[(n - 1) / 2].second; else t = 1.5; //cout << "----------------" << endl; if (!triangle) for (int i=0; i 2*t && a[i].first >= 0) bond[a[i].first].wedge = true; return (t); } void collapse_double_bonds(std::vector &bond, int n_bond, std::vector &atom, double dist) { for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].type == 2 && bond[i].conjoined) // uninitialized value here!!! for (int j = 0; j < n_bond; j++) if (bond[j].exists && j != i && bond[j].type == 1 && bond_length(bond, j, atom) <= dist) { if (bond[j].a == bond[i].a) { bond[j].exists = false; atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[j].b].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[j].b].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].b].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].b].min_y); atom[bond[i].a].max_x = std::max(atom[bond[i].a].max_x, atom[bond[j].b].max_x); atom[bond[i].a].max_y = std::max(atom[bond[i].a].max_y, atom[bond[j].b].max_y); for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == bond[j].b) { bond[k].a = bond[i].a; } else if (bond[k].b == bond[j].b) { bond[k].b = bond[i].a; } } } else if (bond[j].b == bond[i].a) { bond[j].exists = false; atom[bond[i].a].x = (atom[bond[i].a].x + atom[bond[j].a].x) / 2; atom[bond[i].a].y = (atom[bond[i].a].y + atom[bond[j].a].y) / 2; atom[bond[i].a].min_x = std::min(atom[bond[i].a].min_x, atom[bond[j].a].min_x); atom[bond[i].a].min_y = std::min(atom[bond[i].a].min_y, atom[bond[j].a].min_y); atom[bond[i].a].max_x = std::max(atom[bond[i].a].max_x, atom[bond[j].a].max_x); atom[bond[i].a].max_y = std::max(atom[bond[i].a].max_y, atom[bond[j].a].max_y); for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == bond[j].a) { bond[k].a = bond[i].a; } else if (bond[k].b == bond[j].a) { bond[k].b = bond[i].a; } } } else if (bond[j].a == bond[i].b) { bond[j].exists = false; atom[bond[i].b].x = (atom[bond[i].b].x + atom[bond[j].b].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[bond[j].b].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].b].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].b].min_y); atom[bond[i].b].max_x = std::max(atom[bond[i].b].max_x, atom[bond[j].b].max_x); atom[bond[i].b].max_y = std::max(atom[bond[i].b].max_y, atom[bond[j].b].max_y); for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == bond[j].b) { bond[k].a = bond[i].b; } else if (bond[k].b == bond[j].b) { bond[k].b = bond[i].b; } } } else if (bond[j].b == bond[i].b) { bond[j].exists = false; atom[bond[i].b].x = (atom[bond[i].b].x + atom[bond[j].a].x) / 2; atom[bond[i].b].y = (atom[bond[i].b].y + atom[bond[j].a].y) / 2; atom[bond[i].b].min_x = std::min(atom[bond[i].b].min_x, atom[bond[j].a].min_x); atom[bond[i].b].min_y = std::min(atom[bond[i].b].min_y, atom[bond[j].a].min_y); atom[bond[i].b].max_x = std::max(atom[bond[i].b].max_x, atom[bond[j].a].max_x); atom[bond[i].b].max_y = std::max(atom[bond[i].b].max_y, atom[bond[j].a].max_y); for (int k = 0; k < n_bond; k++) if (bond[k].exists) { if (bond[k].a == bond[j].a) { bond[k].a = bond[i].b; } else if (bond[k].b == bond[j].a) { bond[k].b = bond[i].b; } } } } } void find_up_down_bonds(std::vector &bond, int n_bond, std::vector &atom, double thickness) { for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].type == 2) { if (atom[bond[i].a].x > atom[bond[i].b].x) bond_end_swap(bond, i); if (atom[bond[i].a].x == atom[bond[i].b].x && atom[bond[i].a].y > atom[bond[i].b].y) bond_end_swap(bond, i); for (int j = 0; j < n_bond; j++) if (bond[j].exists && bond[j].type == 1 && !bond[j].wedge && !bond[j].hash) { bond[j].down = false; bond[j].up = false; if (bond[j].b == bond[i].a) { double h = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y); if (h > thickness) bond[j].down = true; else if (h < -thickness) bond[j].up = true; } else if (bond[j].a == bond[i].a) { bond_end_swap(bond, j); double h = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y); if (h > thickness) bond[j].down = true; else if (h < -thickness) bond[j].up = true; } else if (bond[j].a == bond[i].b) { double h = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y); if (h > thickness) bond[j].up = true; else if (h < -thickness) bond[j].down = true; } else if (bond[j].b == bond[i].b) { bond_end_swap(bond, j); double h = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y); if (h > thickness) bond[j].up = true; else if (h < -thickness) bond[j].down = true; } } } } void find_old_aromatic_bonds(const potrace_path_t *p, std::vector &bond, int n_bond, std::vector &atom, int n_atom, double avg) { const potrace_path_t *p1 = p; for (int i = 0; i < n_bond; i++) if (bond[i].exists) bond[i].arom = false; while (p != NULL) { if ((p->sign == int('-')) && detect_curve(bond, n_bond, p)) { potrace_path_t *child = p->childlist; if (child != NULL && child->sign == int('+')) { potrace_path_t *gchild = child->childlist; if (gchild != NULL && gchild->sign == int('-')) { for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].curve == p) bond[i].arom = true; delete_curve_with_children(atom, bond, n_atom, n_bond, child); } } } p = p->next; } while (p1 != NULL) { if (p1->sign == int('+') && detect_curve(bond, n_bond, p1)) { potrace_path_t *child = p1->childlist; if (child != NULL && child->sign == int('-')) { std::vector vert; double circum = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].curve == p1) circum += bond_length(bond, i, atom); for (int i = 0; i < n_atom; i++) if (atom[i].exists && atom[i].curve == p1) vert.push_back(i); if (vert.size() > 4) { double diameter = 0, center_x = 0, center_y = 0; int num = 0; for (unsigned int i = 0; i < vert.size(); i++) { for (unsigned int j = i + 1; j < vert.size(); j++) { double dist = distance(atom[vert[i]].x, atom[vert[i]].y, atom[vert[j]].x, atom[vert[j]].y); if (dist > diameter) diameter = dist; } center_x += atom[vert[i]].x; center_y += atom[vert[i]].y; num++; } center_x /= num; center_y /= num; bool centered = true; for (unsigned int i = 0; i < vert.size(); i++) { double dist = distance(atom[vert[i]].x, atom[vert[i]].y, center_x, center_y); if (fabs(dist - diameter / 2) > V_DISPLACEMENT) centered = false; } if (circum < PI * diameter && diameter > avg / 2 && diameter < 3 * avg && centered) { delete_curve_with_children(atom, bond, n_atom, n_bond, p1); for (int i = 0; i < n_bond; i++) if (bond[i].exists) { double dist = distance((atom[bond[i].a].x + atom[bond[i].b].x) / 2, (atom[bond[i].a].y + atom[bond[i].b].y) / 2, center_x, center_y); double ang = angle4(atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[i].a].x, atom[bond[i].a].y, center_x, center_y, atom[bond[i].a].x, atom[bond[i].a].y); ang = acos(ang) * 180.0 / PI; if (ang < 90 && dist < (avg / 3 + diameter / 2)) { bond[i].arom = true; } } } } } } p1 = p1->next; } } void flatten_bonds(std::vector &bond, int n_bond, std::vector &atom, double maxh) { bool found = true; while (found) { found = false; for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].type < 3) { int n = 0; int f = i; double li = bond_length(bond, i, atom); if (atom[bond[i].a].label == " ") { for (int j = 0; j < n_bond; j++) if (j != i && bond[j].exists && bond[j].type < 3 && (bond[i].a == bond[j].a || bond[i].a == bond[j].b)) { n++; f = j; } double lf = bond_length(bond, f, atom); if (n == 1) { if (bond[i].a == bond[f].b) { double h = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].a].x, atom[bond[f].a].y)); double d = distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].a].x, atom[bond[f].a].y); if (h <= maxh && d < 0) { bond[f].exists = false; atom[bond[f].b].exists = false; bond[i].a = bond[f].a; if (lf > li) bond[i].type = bond[f].type; if (bond[f].arom) bond[i].arom = true; if (bond[f].hash) bond[i].hash = true; if (bond[f].wedge) bond[i].wedge = true; found = true; } } else { double h = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].b].x, atom[bond[f].b].y)); double d = distance_from_bond_x_a(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].b].x, atom[bond[f].b].y); if (h <= maxh && d < 0) { bond[f].exists = false; atom[bond[f].a].exists = false; if (bond[f].hash || bond[f].wedge) { bond[i].a = bond[i].b; bond[i].b = bond[f].b; } else bond[i].a = bond[f].b; if (lf > li) bond[i].type = bond[f].type; if (bond[f].arom) bond[i].arom = true; if (bond[f].hash) bond[i].hash = true; if (bond[f].wedge) bond[i].wedge = true; found = true; } } } } n = 0; f = i; if (atom[bond[i].b].label == " ") { for (int j = 0; j < n_bond; j++) if (j != i && bond[j].exists && bond[j].type < 3 && (bond[i].b == bond[j].a || bond[i].b == bond[j].b)) { n++; f = j; } double lf = bond_length(bond, f, atom); if (n == 1) { if (bond[i].b == bond[f].b) { double h = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].a].x, atom[bond[f].a].y)); double d = distance_from_bond_x_b(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].a].x, atom[bond[f].a].y); if (h <= maxh && d > 0) { bond[f].exists = false; atom[bond[f].b].exists = false; if (bond[f].hash || bond[f].wedge) { bond[i].b = bond[i].a; bond[i].a = bond[f].a; } else bond[i].b = bond[f].a; if (lf > li) bond[i].type = bond[f].type; if (bond[f].arom) bond[i].arom = true; if (bond[f].hash) bond[i].hash = true; if (bond[f].wedge) bond[i].wedge = true; found = true; } } else { double h = fabs(distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].b].x, atom[bond[f].b].y)); double d = distance_from_bond_x_b(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[f].b].x, atom[bond[f].b].y); if (h <= maxh && d > 0) { bond[f].exists = false; atom[bond[f].a].exists = false; bond[i].b = bond[f].b; if (lf > li) bond[i].type = bond[f].type; if (bond[f].arom) bond[i].arom = true; if (bond[f].hash) bond[i].hash = true; if (bond[f].wedge) bond[i].wedge = true; found = true; } } } } } } } void mark_terminal_atoms(const std::vector &bond, int n_bond, std::vector &atom, int n_atom) { for (int i = 0; i < n_atom; i++) atom[i].terminal = false; for (int j = 0; j < n_bond; j++) if (bond[j].exists && bond[j].type == 1 && !bond[j].arom) { if (terminal_bond(bond[j].a, j, bond, n_bond)) atom[bond[j].a].terminal = true; if (terminal_bond(bond[j].b, j, bond, n_bond)) atom[bond[j].b].terminal = true; } } void find_limits_on_avg_bond(double &best_bond, const std::vector > &pages_of_avg_bonds, const std::vector > &pages_of_ind_conf) { double max_ind_conf = -FLT_MAX; for (unsigned int l = 0; l < pages_of_ind_conf.size(); l++) for (unsigned int i = 0; i < pages_of_ind_conf[l].size(); i++) if (max_ind_conf < pages_of_ind_conf[l][i]) { max_ind_conf = pages_of_ind_conf[l][i]; best_bond = pages_of_avg_bonds[l][i]; } } void remove_small_bonds_in_chars(std::vector &atom, std::vector &bond, std::vector &letters) { for (int i = 0; i < bond.size(); i++) if (bond[i].exists) for (int j=0; j < letters.size(); j++) if (atom[bond[i].a].x >= letters[j].min_x && atom[bond[i].a].x <= letters[j].max_x && atom[bond[i].a].y >= letters[j].min_y && atom[bond[i].a].y <= letters[j].max_y && atom[bond[i].b].x >= letters[j].min_x && atom[bond[i].b].x <= letters[j].max_x && atom[bond[i].b].y >= letters[j].min_y && atom[bond[i].b].y <= letters[j].max_y) bond[i].exists = false; } bool overlap_boxes(const box_t &a, const box_t &b) { if (a.x1 > b.x2 || b.x1 > a.x2) { return false; } if (a.y2 < b.y1 || b.y2 < a.y1) { return false; } return true; } void remove_bracket_atoms(std::vector &atom, int n_atom, const std::vector &bond, int n_bond, const std::set > &brackets, double thickness, int box_x, int box_y, double box_scale, int real_font_width, int real_font_height, std::vector &reduced_bracket_boxes) { thickness *= box_scale; double scaled_font_width = box_scale * real_font_width; double scaled_font_height = box_scale * real_font_height; thickness = std::max(10., thickness); std::vector connected(atom.size(), 0); for (size_t i = 0; i < n_bond; i++) if (bond[i].exists) { connected[bond[i].a]++; connected[bond[i].b]++; } std::vector confirmed; std::vector available(n_atom, true); for (std::set >::const_iterator j = brackets.begin(); j != brackets.end(); ++j) { bool found(false); for (int i = 0; i < n_atom; i++) { if (atom[i].exists && (atom[i].label == " " || atom[i].label.empty()) && connected[i] < 2 && available[i]) { double x = (double) box_scale * atom[i].x + box_x - FRAME; double y = (double) box_scale * atom[i].y + box_y - FRAME; if (distance(x, y, j->first, j->second) < thickness) { found = true; available[i] = false; } } } if (found) { confirmed.push_back(point_t(j->first, j->second)); } } std::vector > horizontals; for (int i = 0; i < confirmed.size(); i++) for (int j = i + 1; j < confirmed.size(); j++) if ( fabs(confirmed[i].y - confirmed[j].y) < thickness && fabs(confirmed[i].x - confirmed[j].x) > 2 * scaled_font_width) { if (confirmed[i].x < confirmed[j].x) horizontals.push_back(std::make_pair(confirmed[i], confirmed[j])); else horizontals.push_back(std::make_pair(confirmed[j], confirmed[i])); } std::vector bracket_boxes; for (int i = 0; i < horizontals.size(); i++) for (int j = i + 1; j < horizontals.size(); j++) { if ( fabs(horizontals[i].first.x - horizontals[j].first.x) < thickness && fabs(horizontals[i].second.x - horizontals[j].second.x) < thickness && fabs(horizontals[i].first.y - horizontals[j].first.y) > 1.5 * scaled_font_height && fabs(horizontals[i].second.y - horizontals[j].second.y) > 1.5 * scaled_font_height) { box_t b; if (horizontals[i].first.y < horizontals[j].first.y) { b.x1 = horizontals[i].first.x; b.y1 = horizontals[i].first.y; b.x2 = horizontals[j].second.x; b.y2 = horizontals[j].second.y; } else { b.x1 = horizontals[j].first.x; b.y1 = horizontals[j].first.y; b.x2 = horizontals[i].second.x; b.y2 = horizontals[i].second.y; } bracket_boxes.push_back(b); } } for (int i = 0; i < bracket_boxes.size(); i++) { if (bracket_boxes[i].x1 != -1) { box_t b = bracket_boxes[i]; for (int k = 0; k < n_atom; k++) { if (atom[k].exists && (atom[k].label == " " || atom[k].label.empty()) && connected[k] < 2) { double x = (double) box_scale * atom[k].x + box_x - FRAME; double y = (double) box_scale * atom[k].y + box_y - FRAME; if (distance(x, y, b.x1, b.y1) < thickness || distance(x, y, b.x1, b.y2) < thickness || distance(x, y, b.x2, b.y1) < thickness || distance(x, y, b.x2, b.y2) < thickness ) { atom[k].exists = false; } } } } } for (int i = 0; i < bracket_boxes.size(); i++) { if (bracket_boxes[i].x1 != -1) { box_t b = bracket_boxes[i]; for (int j = i + 1; j < bracket_boxes.size(); j++) { if (bracket_boxes[j].x1 != -1) { if (overlap_boxes(b, bracket_boxes[j])) { b.x1 = std::min(b.x1, bracket_boxes[j].x1); b.y1 = std::min(b.y1, bracket_boxes[j].y1); b.x2 = std::max(b.x2, bracket_boxes[j].x2); b.y2 = std::max(b.y2, bracket_boxes[j].y2); bracket_boxes[j].x1 = -1; } } } b.x1 = int(double(b.x1 + FRAME - box_x) / box_scale); b.y1 = int(double(b.y1 + FRAME - box_y) / box_scale); b.x2 = int(double(b.x2 + FRAME - box_x) / box_scale); b.y2 = int(double(b.y2 + FRAME - box_y) / box_scale); bracket_t bracket; bracket.box = b; reduced_bracket_boxes.push_back(bracket); //cout << b.x1 << " " << b.y1 << " " << b.x2 << " " << b.y2 << endl; } } } void remove_vertical_bonds_close_to_brackets( const std::vector &bracket_boxes, std::vector &atom, const std::vector &bond, int n_bond, double thickness, double avg_bond_length) { std::vector connected(atom.size(), 0); for (size_t i = 0; i < n_bond; i++) if (bond[i].exists) { connected[bond[i].a]++; connected[bond[i].b]++; } for (int i = 0; i < bracket_boxes.size(); i++) { int x1 = bracket_boxes[i].box.x1; int y1 = bracket_boxes[i].box.y1; int x2 = bracket_boxes[i].box.x2; int y2 = bracket_boxes[i].box.y2; for (size_t j = 0; j < n_bond; j++) if (bond[j].exists) { int a = bond[j].a; int b = bond[j].b; if (fabs(atom[a].x - atom[b].x) < 2 * thickness) { if (connected[a] == 1) { double x = (double) atom[a].x; double y = (double) atom[a].y; if ((fabs(y - y1) < 3 * thickness || fabs(y - y2) < 3 * thickness) && (fabs(x1 - x) < avg_bond_length / 2 || fabs(x - x2) < avg_bond_length / 2)) { atom[a].exists = false; continue; } } if (connected[b] == 1) { double x = (double) atom[b].x; double y = (double) atom[b].y; if ( (fabs(y - y1) < 3 * thickness || fabs(y - y2) < 3 * thickness) && (fabs(x1 - x) < avg_bond_length / 2 || fabs(x - x2) < avg_bond_length / 2)) { atom[b].exists = false; continue; } } } } } } void assign_labels_to_brackets(std::vector &bracket_boxes, const std::vector &label, int n_label, const std::vector &letters, int n_letters, int real_font_width, int real_font_height) { for (int j = 0; j < bracket_boxes.size(); j++) { box_t b = bracket_boxes[j].box; std::string a; double d = DBL_MAX; for (int i = 0; i < n_label; i++) if ((label[i].a)[0] != '+' && (label[i].a)[0] != '-') { double x = (double) label[i].x1; double y = (double) label[i].y1; if (fabs(x - b.x2) < 2 * real_font_width && fabs(y - b.y2) < 2 * real_font_height) { double dist = (x - b.x2) * (x - b.x2) + (y - b.y2) * (y - b.y2); if (dist < d) { d = dist; a = label[i].a; } } } for (int i = 0; i < n_letters; i++) if (letters[i].free && letters[i].a != '+' && letters[i].a != '-') { double x = (double) letters[i].x; double y = (double) letters[i].y; if (fabs(x - b.x2) < 2 * real_font_width && fabs(y - b.y2) < 2 * real_font_height) { double dist = (x - b.x2) * (x - b.x2) + (y - b.y2) * (y - b.y2); if (dist < d) { d = dist; a = letters[i].a; } } } bracket_boxes[j].a = a; } } void remove_high_order_bonds_connected_to_hash_bonds(std::vector &bond, int n_bond, std::vector &atom, double avg) { for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].type > 2 && bond_length(bond, i, atom) < avg/2) { if (terminal_bond(bond[i].a, i, bond, n_bond) && atom[bond[i].a].label == " ") { for (int j = 0; j < n_bond; j++) if (j != i && bond[j].exists && bond[j].hash && (bond[j].a == bond[i].b || bond[j].b == bond[i].b)) { bond[i].exists = false; atom[bond[i].a].exists = false; break; } } if (terminal_bond(bond[i].b, i, bond, n_bond) && atom[bond[i].b].label == " ") { for (int j = 0; j < n_bond; j++) if (j != i && bond[j].exists && bond[j].hash && (bond[j].a == bond[i].a || bond[j].b == bond[i].a)) { bond[i].exists = false; atom[bond[i].b].exists = false; break; } } } } // US20050049267A1-20050303-C04374.png osra-2.1.3/src/unpaper.cpp0000664000175000017500000040727614115175251014134 0ustar igorigor/* --------------------------------------------------------------------------- unpaper - written by Jens Gulden 2005-2007 */ const char* VERSION = "0.3"; /* --------------------------------------------------------------------------- unpaper is a post-processing tool for scanned sheets of paper, especially for book pages that have been scanned from previously created photocopies. The main purpose is to make scanned book pages better readable on screen after conversion to PDF. Additionally, unpaper might be useful to enhance the quality of scanned pages before performing optical character recognition (OCR). unpaper tries to clean scanned images by removing dark edges that appeared through scanning or copying on areas outside the actual page content (e.g. dark areas between the left-hand-side and the right-hand-side of a double- sided book-page scan). The program also tries to detect disaligned centering and rotation of pages and will automatically straighten each page by rotating it to the correct angle. This process is called "deskewing". Note that the automatic processing will sometimes fail. It is always a good idea to manually control the results of unpaper and adjust the parameter settings according to the requirements of the input. Each processing step can also be disabled individually for each sheet. Input and output files can be in either .pbm , .pgm or .ppm format, thus generally in .pnm format, as also used by the Linux scanning tools scanimage and scanadf. Conversion to PDF can e.g. be achieved with the Linux tools pgm2tiff, tiffcp and tiff2pdf. --------------------------------------------------------------------------- */ //const char* COMPILE = // "gcc -D TIMESTAMP=\"\" -lm -O3 -funroll-all-loops -fomit-frame-pointer -ftree-vectorize -o unpaper unpaper.c\n"; /* ------------------------------------------------------------------------ */ #include #include #include #include #include #include /* --- preprocessor macros ------------------------------------------------ */ #define abs(value) ( (value) >=0 ? (value) : -(value) ) #define max(a, b) ( (a >= b) ? (a) : (b) ) #define pluralS(i) ( (i > 1) ? "s" : "" ) #define pixelValue(r, g, b) ( (r)<<16 | (g)<<8 | (b) ) #define pixelGrayscaleValue(g) ( (g)<<16 | (g)<<8 | (g) ) #define pixelGrayscale(r, g, b) ( ( ( r == g ) && ( r == b ) ) ? r : ( ( r + g + b ) / 3 ) ) // average (optimized for already gray values) #define pixelLightness(r, g, b) ( r < g ? ( r < b ? r : b ) : ( g < b ? g : b ) ) // minimum #define pixelDarknessInverse(r, g, b) ( r > g ? ( r > b ? r : b ) : ( g > b ? g : b ) ) // maximum #define red(pixel) ( (pixel >> 16) & 0xff ) #define green(pixel) ( (pixel >> 8) & 0xff ) #define blue(pixel) ( pixel & 0xff ) /* --- preprocessor constants ---------------------------------------------- */ #define MAX_MULTI_INDEX 10000 // maximum pixel count of virtual line to detect rotation with #define MAX_ROTATION_SCAN_SIZE 10000 // maximum pixel count of virtual line to detect rotation with #define MAX_MASKS 100 #define MAX_POINTS 100 #define MAX_FILES 100 #define MAX_PAGES 2 #define WHITE 255 #define GRAY 127 #define BLACK 0 #define BLANK_TEXT "" /* --- typedefs ----------------------------------------------------------- */ typedef enum { FALSE, TRUE } BOOLEAN; typedef enum { VERBOSE_QUIET = -1, VERBOSE_NONE = 0, VERBOSE_NORMAL = 1, VERBOSE_MORE = 2, VERBOSE_DEBUG = 3, VERBOSE_DEBUG_SAVE = 4 } VERBOSE_LEVEL; typedef enum { X, Y, COORDINATES_COUNT } COORDINATES; typedef enum { WIDTH, HEIGHT, DIMENSIONS_COUNT } DIMENSIONS; typedef enum { HORIZONTAL, VERTICAL, DIRECTIONS_COUNT } DIRECTIONS; typedef enum { LEFT, TOP, RIGHT, BOTTOM, EDGES_COUNT } EDGES; typedef enum { LAYOUT_NONE, LAYOUT_SINGLE, LAYOUT_DOUBLE, LAYOUTS_COUNT } LAYOUTS; typedef enum { BRIGHT, DARK, SHADINGS_COUNT } SHADINGS; typedef enum { RED, GREEN, BLUE, COLORCOMPONENTS_COUNT } COLORCOMPONENTS; typedef enum { PBM, PGM, PPM, FILETYPES_COUNT } FILETYPES; /* --- struct ------------------------------------------------------------- */ struct IMAGE { unsigned char* buffer; unsigned char* bufferGrayscale; unsigned char* bufferLightness; unsigned char* bufferDarknessInverse; int width; int height; int bitdepth; BOOLEAN color; int background; }; /* --- constants ---------------------------------------------------------- */ // file type names (see typedef FILETYPES) const char FILETYPE_NAMES[FILETYPES_COUNT][5] = { "pbm", "pgm", "ppm" }; // factors for conversion to inches #define MEASUREMENTS_COUNT 3 const char MEASUREMENTS[MEASUREMENTS_COUNT][2][15] = { { "in", "1.0" }, { "cm", "0.393700787" }, { "mm", "0.0393700787" } }; // papersize alias names #define PAPERSIZES_COUNT 10 const char PAPERSIZES[PAPERSIZES_COUNT][2][50] = { { "a5", "14.8cm,21cm" }, { "a5-landscape", "21cm,14.8cm" }, { "a4", "21cm,29.7cm" }, { "a4-landscape", "29.7cm,21cm" }, { "a3", "29.7cm,42cm" }, { "a3-landscape", "42cm,29.7cm" }, { "letter", "8.5in,11in" }, { "letter-landscape", "11in,8.5in" }, { "legal", "8.5in,14in" }, { "legal-landscape", "14in,8.5in" } }; // color alias names #define COLORS_COUNT 2 const char COLORS[COLORS_COUNT][2][50] = { { "black", "#000000" }, { "white", "#ffffff" } }; /* --- global variable ---------------------------------------------------- */ VERBOSE_LEVEL verbose; #include "unpaper.h" /**************************************************************************** * tool functions * ****************************************************************************/ /* --- arithmetic tool functions ------------------------------------------ */ /** * Returns the quadratic square of a number. */ double sqr(double d) { return d*d; } /** * Converts degrees to radians. */ double degreesToRadians(double d) { return d * M_PI / 180.0; } /** * Converts radians to degrees. */ double radiansToDegrees(double r) { return r * 180.0 / M_PI; } /** * Limits an integer value to a maximum. */ void limit(int* i, int max) { if (*i > max) { *i = max; } } /* --- tool functions for parameter parsing and verbose output ------------ */ /** * Parses a parameter string on occurrences of 'vertical', 'horizontal' or both. */ int parseDirections(char* s, int* exitCode) { int dir = 0; if (strchr(s, 'h') != 0) // (there is no 'h' in 'vertical'...) { dir = 1< 0) { if (s[0] != NULL) { strcpy(buf, s[0]); } else { strcpy(buf, BLANK_TEXT); } for (i = 1; i < cnt; i++) { if (s[i] != NULL) { sprintf(buf, "%s, %s", buf, s[i]); } else { sprintf(buf, "%s, %s", buf, BLANK_TEXT); } } } else { buf[0] = 0; //strcpy(buf, ""); } return buf; } /** * Parses a string at argv[*i] argument consisting of comma-concatenated * integers. The string may also be of a different format, in which case * *i remains unchanged and *multiIndexCount is set to -1. * * @see isInMultiIndex(..) */ void parseMultiIndex(int* i, char* argv[], int multiIndex[], int* multiIndexCount) { char s1[MAX_MULTI_INDEX * 5]; // buffer char s2[MAX_MULTI_INDEX * 5]; // buffer char c; int index; int j; (*i)++; *multiIndexCount = 0; if (argv[*i][0] != '-') // not another option directly following { strcpy(s1, argv[*i]); // argv[*i] -> s1 do { index = -1; s2[0] = (char)0; // = "" sscanf(s1, "%d%c%s", &index, &c, s2); if (index != -1) { multiIndex[(*multiIndexCount)++] = index; if (c=='-') // range is specified: get range end { strcpy(s1, s2); // s2 -> s1 sscanf(s1, "%d,%s", &index, s2); for (j = multiIndex[(*multiIndexCount)-1]+1; j <= index; j++) { multiIndex[(*multiIndexCount)++] = j; } } } else { // string is not correctly parseable: break without inreasing *i (string may be e.g. input-filename) *multiIndexCount = -1; // disable all (*i)--; return; // exit here } strcpy(s1, s2); // s2 -> s1 } while ((*multiIndexCount < MAX_MULTI_INDEX) && (strlen(s1) > 0)); } else // no explicit list of sheet-numbers given { *multiIndexCount = -1; // disable all (*i)--; return; } } /** * Tests whether an integer is included in the array of integers given as multiIndex. * If multiIndexCount is -1, each possible integer is considered to be in the * multiIndex list. * * @see parseMultiIndex(..) */ BOOLEAN isInMultiIndex(int index, int multiIndex[MAX_MULTI_INDEX], int multiIndexCount) { int i; if (multiIndexCount == -1) { return TRUE; // all } else { for (i = 0; i < multiIndexCount; i++) { if (index == multiIndex[i]) { return TRUE; // found in list } } return FALSE; // not found in list } } /** * Tests whether 'index' is either part of multiIndex or excludeIndex. * (Throughout the application, excludeIndex generalizes several individual * multi-indices: if an entry is part of excludeIndex, it is treated as being * an entry of all other multiIndices, too.) */ BOOLEAN isExcluded(int index, int multiIndex[MAX_MULTI_INDEX], int multiIndexCount, int excludeIndex[MAX_MULTI_INDEX], int excludeIndexCount) { if ( (isInMultiIndex(index, excludeIndex, excludeIndexCount) == TRUE) || (isInMultiIndex(index, multiIndex, multiIndexCount) == TRUE) ) return(TRUE); else return(FALSE); } /** * Outputs all entries in an array of integer to the console. */ void printMultiIndex(int multiIndex[MAX_MULTI_INDEX], int multiIndexCount) { int i; if (multiIndexCount == -1) { printf("all"); } else if (multiIndexCount == 0) { printf("none"); } else { for (i = 0; i < multiIndexCount; i++) { printf("%d", multiIndex[i]); if (i < multiIndexCount-1) { printf(","); } } } printf("\n"); } /** * Tests if a point is covered by a mask. */ BOOLEAN inMask(int x, int y, int mask[EDGES_COUNT]) { if ( (x >= mask[LEFT]) && (x <= mask[RIGHT]) && (y >= mask[TOP]) && (y <= mask[BOTTOM]) ) { return TRUE; } else { return FALSE; } } /** * Tests if masks a and b overlap. */ BOOLEAN masksOverlap(int a[EDGES_COUNT], int b[EDGES_COUNT]) { if ( inMask(a[LEFT], a[TOP], b) || inMask(a[RIGHT], a[BOTTOM], b) ) return(TRUE); else return(FALSE); } /** * Tests if at least one mask in masks overlaps with m. */ BOOLEAN masksOverlapAny(int m[EDGES_COUNT], int masks[MAX_MASKS][EDGES_COUNT], int masksCount) { int i; for ( i = 0; i < masksCount; i++ ) { if ( masksOverlap(m, masks[i]) ) { return TRUE; } } return FALSE; } /* --- tool functions for image handling ---------------------------------- */ /** * Allocates a memory block for storing image data and fills the IMAGE-struct * with the specified values. */ void initImage(struct IMAGE* image, int width, int height, int bitdepth, BOOLEAN color, int background) { int size; size = width * height; if ( color ) { image->bufferGrayscale = (unsigned char*)malloc(size); image->bufferLightness = (unsigned char*)malloc(size); image->bufferDarknessInverse = (unsigned char*)malloc(size); memset(image->bufferGrayscale, background, size); memset(image->bufferLightness, background, size); memset(image->bufferDarknessInverse, background, size); size *= 3; } image->buffer = (unsigned char*)malloc(size); memset(image->buffer, background, size); if ( ! color ) { image->bufferGrayscale = image->buffer; image->bufferLightness = image->buffer; image->bufferDarknessInverse = image->buffer; } image->width = width; image->height = height; image->bitdepth = bitdepth; image->color = color; image->background = background; } /** * Frees an image. */ void freeImage(struct IMAGE* image) { free(image->buffer); if (image->color) { free(image->bufferGrayscale); free(image->bufferLightness); free(image->bufferDarknessInverse); } } /** * Replaces one image with another. */ void replaceImage(struct IMAGE* image, struct IMAGE* newimage) { freeImage(image); // pass-back new image *image = *newimage; // copy whole struct } /** * Sets the color/grayscale value of a single pixel. * * @return TRUE if the pixel has been changed, FALSE if the original color was the one to set */ BOOLEAN setPixel(int pixel, int x, int y, struct IMAGE* image) { unsigned char* p; int w, h; int pos; BOOLEAN result; unsigned char r, g, b; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return FALSE; //nop } else { pos = (y * w) + x; r = (pixel >> 16) & 0xff; g = (pixel >> 8) & 0xff; b = pixel & 0xff; if ( ! image->color ) { p = &image->buffer[pos]; if ((r == g) && (r == b)) // optimization (avoid division by 3) { pixel = r; } else { pixel = pixelGrayscale(r, g, b); // convert to gray (will already be in most cases, but we can't be sure) } if (*p != (unsigned char)pixel) { *p = (unsigned char)pixel; return TRUE; } else { return FALSE; } } else // color { result = FALSE; p = &image->buffer[pos*3]; if (*p != r) { *p = r; result = TRUE; } p++; if (*p != g) { *p = g; result = TRUE; } p++; if (*p != b) { *p = b; result = TRUE; } if ( result ) // modified: update cached grayscale, lightness and brightnessInverse values { image->bufferGrayscale[pos] = pixelGrayscale(r, g, b); image->bufferLightness[pos] = pixelLightness(r, g, b); image->bufferDarknessInverse[pos] = pixelDarknessInverse(r, g, b); } return result; } } } /** * Returns the color or grayscale value of a single pixel. * Always returns a color-compatible value (which may be interpreted as 8-bit grayscale) * * @return color or grayscale-value of the requested pixel, or WHITE if the coordinates are outside the image */ int getPixel(int x, int y, struct IMAGE* image) { int w, h; int pos; int pix; unsigned char r, g, b; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return pixelValue(WHITE, WHITE, WHITE); } else { pos = (y * w) + x; if ( ! image->color ) { pix = (unsigned char)image->buffer[pos]; return pixelValue(pix, pix, pix); } else // color { pos *= 3; r = (unsigned char)image->buffer[pos++]; g = (unsigned char)image->buffer[pos++]; b = (unsigned char)image->buffer[pos]; return pixelValue(r, g, b); } } } /** * Returns a color component of a single pixel (0-255). * * @param colorComponent either RED, GREEN or BLUE * @return color or grayscale-value of the requested pixel, or WHITE if the coordinates are outside the image */ /* (currently not used) int getPixelComponent(int x, int y, int colorComponent, struct IMAGE* image) { int w, h; int pos; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return WHITE; } else { pos = (y * w) + x; if ( ! image->color ) { return (unsigned char)image->buffer[pos]; } else { // color return (unsigned char)image->buffer[(pos * 3) + colorComponent]; } } } */ /** * Returns the grayscale (=brightness) value of a single pixel. * * @return grayscale-value of the requested pixel, or WHITE if the coordinates are outside the image */ int getPixelGrayscale(int x, int y, struct IMAGE* image) { int w, h; int pos; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return WHITE; } else { pos = (y * w) + x; return image->bufferGrayscale[pos]; } } /** * Returns the 'lightness' value of a single pixel. For color images, this * value denotes the minimum brightness of a single color-component in the * total color, which means that any color is considered 'dark' which has * either the red, the green or the blue component (or, of course, several * of them) set to a high value. In some way, this is a measure how close a * color is to white. * For grayscale images, this value is equal to the pixel brightness. * * @return lightness-value (the higher, the lighter) of the requested pixel, or WHITE if the coordinates are outside the image */ int getPixelLightness(int x, int y, struct IMAGE* image) { int w, h; int pos; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return WHITE; } else { pos = (y * w) + x; return image->bufferLightness[pos]; } } /** * Returns the 'inverse-darkness' value of a single pixel. For color images, this * value denotes the maximum brightness of a single color-component in the * total color, which means that any color is considered 'light' which has * either the red, the green or the blue component (or, of course, several * of them) set to a high value. In some way, this is a measure how far away a * color is to black. * For grayscale images, this value is equal to the pixel brightness. * * @return inverse-darkness-value (the LOWER, the darker) of the requested pixel, or WHITE if the coordinates are outside the image */ int getPixelDarknessInverse(int x, int y, struct IMAGE* image) { int w, h; int pos; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return WHITE; } else { pos = (y * w) + x; return image->bufferDarknessInverse[pos]; } } /** * Sets the color/grayscale value of a single pixel to either black or white. * * @return TRUE if the pixel has been changed, FALSE if the original color was the one to set */ BOOLEAN setPixelBW(int x, int y, struct IMAGE* image, int blackwhite) { unsigned char* p; int w, h; int pos; BOOLEAN result; w = image->width; h = image->height; if ( (x < 0) || (x >= w) || (y < 0) || (y >= h) ) { return FALSE; //nop } else { pos = (y * w) + x; if ( ! image->color ) { p = &image->buffer[pos]; if (*p != blackwhite) { *p = blackwhite; return TRUE; } else { return FALSE; } } else // color { p = &image->buffer[pos * 3]; result = FALSE; if (*p != blackwhite) { *p = blackwhite; result = TRUE; } p++; if (*p != blackwhite) { *p = blackwhite; result = TRUE; } p++; if (*p != blackwhite) { *p = blackwhite; result = TRUE; } return result; } } } /** * Sets the color/grayscale value of a single pixel to white. * * @return TRUE if the pixel has been changed, FALSE if the original color was the one to set */ BOOLEAN clearPixel(int x, int y, struct IMAGE* image) { return setPixelBW(x, y, image, WHITE); } /** * Clears a rectangular area of pixels with either black or white. * @return The number of pixels actually changed from black (dark) to white. */ int clearRect(int left, int top, int right, int bottom, struct IMAGE* image, int blackwhite) { int x; int y; int count; count = 0; for (y = top; y <= bottom; y++) { for (x = left; x <= right; x++) { if (setPixelBW(x, y, image, blackwhite)) { count++; } } } return count; } /** * Copies one area of an image into another. */ void copyImageArea(int x, int y, int width, int height, struct IMAGE* source, int toX, int toY, struct IMAGE* target) { int row; int col; int pixel; // naive but generic implementation for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { pixel = getPixel(x+col, y+row, source); setPixel(pixel, toX+col, toY+row, target); } } } /** * Copies a whole image into another. */ void copyImage(struct IMAGE* source, int toX, int toY, struct IMAGE* target) { copyImageArea(0, 0, source->width, source->height, source, toX, toY, target); } /** * Centers one area of an image inside an area of another image. * If the source area is smaller than the target area, is is equally * surrounded by a white border, if it is bigger, it gets equally cropped * at the edges. */ void centerImageArea(int x, int y, int w, int h, struct IMAGE* source, int toX, int toY, int ww, int hh, struct IMAGE* target) { if ((w < ww) || (h < hh)) // white rest-border will remain, so clear first { clearRect(toX, toY, toX + ww - 1, toY + hh - 1, target, target->background); } if (w < ww) { toX += (ww - w) / 2; } if (h < hh) { toY += (hh - h) / 2; } if (w > ww) { x += (w - ww) / 2; w = ww; } if (h > hh) { y += (h - hh) / 2; h = hh; } copyImageArea(x, y, w, h, source, toX, toY, target); } /** * Centers a whole image inside an area of another image. */ void centerImage(struct IMAGE* source, int toX, int toY, int ww, int hh, struct IMAGE* target) { centerImageArea(0, 0, source->width, source->height, source, toX, toY, ww, hh, target); } /** * Returns the average brightness of a rectagular area. */ int brightnessRect(int x1, int y1, int x2, int y2, struct IMAGE* image) { int x; int y; int pixel; int total; int count; total = 0; count = (x2-x1+1)*(y2-y1+1); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { pixel = getPixelGrayscale(x, y, image); total += pixel; } } return total / count; } /** * Returns the average lightness of a rectagular area. */ int lightnessRect(int x1, int y1, int x2, int y2, struct IMAGE* image) { int x; int y; int pixel; int total; int count; total = 0; count = (x2-x1+1)*(y2-y1+1); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { pixel = getPixelLightness(x, y, image); total += pixel; } } return total / count; } /** * Returns the average darkness of a rectagular area. */ int darknessInverseRect(int x1, int y1, int x2, int y2, struct IMAGE* image) { int x; int y; int pixel; int total; int count; total = 0; count = (x2-x1+1)*(y2-y1+1); for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { pixel = getPixelDarknessInverse(x, y, image); total += pixel; } } return total / count; } /** * Counts the number of pixels in a rectangular area whose grayscale * values ranges between minColor and maxBrightness. Optionally, the area can get * cleared with white color while counting. */ int countPixelsRect(int left, int top, int right, int bottom, int minColor, int maxBrightness, BOOLEAN clear, struct IMAGE* image) { int x; int y; int pixel; int count; count = 0; for (y = top; y <= bottom; y++) { for (x = left; x <= right; x++) { pixel = getPixelGrayscale(x, y, image); if ((pixel>=minColor) && (pixel <= maxBrightness)) { if (clear==TRUE) { clearPixel(x, y, image); } count++; } } } return count; } /** * Counts the number of dark pixels around the pixel at (x,y), who have a * square-metric distance of 'level' to the (x,y) (thus, testing the values * of 9 pixels if level==1, 16 pixels if level==2 and so on). * Optionally, the pixels can get cleared after counting. */ int countPixelNeighborsLevel(int x, int y, BOOLEAN clear, int level, int whiteMin, struct IMAGE* image) { int xx; int yy; int count; int pixel; count = 0; // upper and lower rows for (xx = x - level; xx <= x + level; xx++) { // upper row pixel = getPixelLightness(xx, y - level, image); if (pixel < whiteMin) // non-light pixel found { if (clear == TRUE) { clearPixel(xx, y - level, image); } count++; } // lower row pixel = getPixelLightness(xx, y + level, image); if (pixel < whiteMin) { if (clear == TRUE) { clearPixel(xx, y + level, image); } count++; } } // middle rows for (yy = y-(level-1); yy <= y+(level-1); yy++) { // first col pixel = getPixelLightness(x - level, yy, image); if (pixel < whiteMin) { if (clear == TRUE) { clearPixel(x - level, yy, image); } count++; } // last col pixel = getPixelLightness(x + level, yy, image); if (pixel < whiteMin) { if (clear == TRUE) { clearPixel(x + level, yy, image); } count++; } } /* old version, not optimized: for (yy = y-level; yy <= y+level; yy++) { for (xx = x-level; xx <= x+level; xx++) { if (abs(xx-x)==level || abs(yy-y)==level) { pixel = getPixelLightness(xx, yy, image); if (pixel < whiteMin) { if (clear==TRUE) { clearPixel(xx, yy, image); } count++; } } } }*/ return count; } /** * Count all dark pixels in the distance 0..intensity that are directly * reachable from the dark pixel at (x,y), without having to cross bright * pixels. */ int countPixelNeighbors(int x, int y, int intensity, int whiteMin, struct IMAGE* image) { int level; int count; int lCount; count = 1; // assume self as set lCount = -1; for (level = 1; (lCount != 0) && (level <= intensity); level++) // can finish when one level is completely zero { lCount = countPixelNeighborsLevel(x, y, FALSE, level, whiteMin, image); count += lCount; } return count; } /** * Clears all dark pixels that are directly reachable from the dark pixel at * (x,y). This should be called only if it has previously been detected that * the amount of pixels to clear will be reasonable small. */ void clearPixelNeighbors(int x, int y, int whiteMin, struct IMAGE* image) { int level; int lCount; clearPixel(x, y, image); lCount = -1; for (level = 1; lCount != 0; level++) // lCount will become 0, otherwise countPixelNeighbors() would previously have delivered a bigger value (and this here would not have been called) { lCount = countPixelNeighborsLevel(x, y, TRUE, level, whiteMin, image); } } /** * Flood-fill an area of pixels. * (Declaration of header for indirect recursive calls.) */ void floodFill(int x, int y, int color, int maskMin, int maskMax, int intensity, struct IMAGE* image); /** * Solidly fills a line of pixels heading towards a specified direction * until color-changes in the pixels to overwrite exceed the 'intensity' * parameter. * * @param stepX either -1 or 1, if stepY is 0, else 0 * @param stepY either -1 or 1, if stepX is 0, else 0 */ int fillLine(int x, int y, int stepX, int stepY, int color, int maskMin, int maskMax, int intensity, struct IMAGE* image) { int pixel; int distance; int intensityCount; int w, h; w = image->width; h = image->height; distance = 0; intensityCount = 1; // first pixel must match, otherwise directly exit while (1==1) // ! { x += stepX; y += stepY; pixel = getPixelGrayscale(x, y, image); if ((pixel>=maskMin) && (pixel<=maskMax)) { intensityCount = intensity; // reset counter } else { intensityCount--; // allow maximum of 'intensity' pixels to be bright, until stop } if ((intensityCount > 0) && (x>=0) && (x=0) && (y=maskMin) && (pixel<=maskMax)) { // first, fill a 'cross' (both vertical, horizontal line) setPixel(color, x, y, image); left = fillLine(x, y, -1, 0, color, maskMin, maskMax, intensity, image); top = fillLine(x, y, 0, -1, color, maskMin, maskMax, intensity, image); right = fillLine(x, y, 1, 0, color, maskMin, maskMax, intensity, image); bottom = fillLine(x, y, 0, 1, color, maskMin, maskMax, intensity, image); // now recurse on each neighborhood-pixel of the cross (most recursions will immediately return) floodFillAroundLine(x, y, -1, 0, left, color, maskMin, maskMax, intensity, image); floodFillAroundLine(x, y, 0, -1, top, color, maskMin, maskMax, intensity, image); floodFillAroundLine(x, y, 1, 0, right, color, maskMin, maskMax, intensity, image); floodFillAroundLine(x, y, 0, 1, bottom, color, maskMin, maskMax, intensity, image); } } /* --- tool function for file handling ------------------------------------ */ /** * Tests if a file exists. */ BOOLEAN fileExists(const char* filename) { FILE *f; f = fopen(filename,"r"); if (f == NULL) { return FALSE; } else { fclose(f); return TRUE; } } /** * Loads image data from a file in pnm format. * * @param filename name of file to load * @param image structure to hold loaded image * @param type returns the type of the loaded image * @return TRUE on success, FALSE on failure */ BOOLEAN loadImage(char* filename, struct IMAGE* image, int* type) { FILE *f; int fileSize; int bytesPerLine; char magic[10]; char word[255]; char c; int maxColorIndex; int inputSize; int inputSizeFile; int read; unsigned char* buffer2; int lineOffsetInput; int lineOffsetOutput; int x; int y; int bb; int off; int bits; int bit; int pixel; int size; int pos; unsigned char* p; unsigned char r, g, b; // open input file f = fopen(filename, "rb"); if (f == NULL) { return FALSE; } // get file size fseek(f, 0, SEEK_END); // to end fileSize = ftell(f); rewind(f); // back to start // read magic number fread(magic, 1, 2, f); magic[2] = 0; // terminate if (strcmp(magic, "P4")==0) { *type = PBM; image->bitdepth = 1; image->color = FALSE; } else if (strcmp(magic, "P5")==0) { *type = PGM; image->bitdepth = 8; image->color = FALSE; } else if (strcmp(magic, "P6")==0) { *type = PPM; image->bitdepth = 8; image->color = TRUE; } else { return FALSE; } // get image info: width, height, optionally depth fgetc(f); // skip \n after magic number fscanf(f, "%s", word); while (word[0]=='#') // skip comment lines { do { fscanf(f, "%c", &c); } while ((feof(f)==0)&&(c!='\n')); fscanf(f, "%s", word); } // now reached width/height pair as decimal ascii sscanf(word, "%d", &image->width); fscanf(f, "%d", &image->height); fgetc(f); // skip \n after width/height pair if (*type == PBM) { bytesPerLine = (image->width + 7) / 8; } else // PGM or PPM { fscanf(f, "%s", word); while (word[0]=='#') // skip comment lines { do { fscanf(f, "%c", &c); } while ((feof(f) == 0) && (c != '\n')); fscanf(f, "%s", word); } // read max color value sscanf(word, "%d", &maxColorIndex); fgetc(f); // skip \n after max color index if (maxColorIndex > 255) { return FALSE; } bytesPerLine = image->width; if (*type == PPM) { bytesPerLine *= 3; // 3 color-components per pixel } } // read binary image data inputSizeFile = fileSize - ftell(f); inputSize = bytesPerLine * image->height; image->buffer = (unsigned char*)malloc(inputSize); read = fread(image->buffer, 1, inputSize, f); if (read != inputSize) { return FALSE; } if (*type == PBM) // internally convert b&w to 8-bit for processing { buffer2 = (unsigned char*)malloc(image->width * image->height); lineOffsetInput = 0; lineOffsetOutput = 0; for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { bb = x >> 3; // x / 8; off = x & 7; // x % 8; bit = 128>>off; bits = image->buffer[lineOffsetInput + bb]; bits &= bit; if (bits == 0) // 0: white pixel { pixel = 0xff; } else { pixel = 0x00; } buffer2[lineOffsetOutput+x] = pixel; // set as whole byte } lineOffsetInput += bytesPerLine; lineOffsetOutput += image->width; } free(image->buffer); image->buffer = buffer2; } fclose(f); if (*type == PPM) { // init cached values for grayscale, lightness and darknessInverse size = image->width * image->height; image->bufferGrayscale = (unsigned char*)malloc(size); image->bufferLightness = (unsigned char*)malloc(size); image->bufferDarknessInverse = (unsigned char*)malloc(size); p = image->buffer; for (pos = 0; pos < size; pos++) { r = *p; p++; g = *p; p++; b = *p; p++; image->bufferGrayscale[pos] = pixelGrayscale(r, g, b); image->bufferLightness[pos] = pixelLightness(r, g, b); image->bufferDarknessInverse[pos] = pixelDarknessInverse(r, g, b); } } else { image->bufferGrayscale = image->buffer; image->bufferLightness = image->buffer; image->bufferDarknessInverse = image->buffer; } return TRUE; } /** * Saves image data to a file in pgm or pbm format. * * @param filename name of file to save * @param image image to save * @param type filetype of the image to save * @param overwrite allow overwriting existing files * @param blackThreshold threshold for grayscale-to-black&white conversion * @return TRUE on success, FALSE on failure */ BOOLEAN saveImage(const char* filename, struct IMAGE* image, int type, BOOLEAN overwrite, float blackThreshold) { unsigned char* buf; int bytesPerLine; int inputSize; int outputSize; int lineOffsetOutput; int offsetInput; int offsetOutput; int x; int y; int pixel; int b; int off; unsigned char bit; unsigned char val; const char* outputMagic; FILE* outputFile; int blackThresholdAbs; BOOLEAN result; result = TRUE; if (type == PBM) // convert to pbm { blackThresholdAbs = WHITE * (1.0 - blackThreshold); bytesPerLine = (image->width + 7) >> 3; // / 8; outputSize = bytesPerLine * image->height; buf = (unsigned char*)malloc(outputSize); memset(buf, 0, outputSize); lineOffsetOutput = 0; for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { pixel = getPixelGrayscale(x, y, image); b = x >> 3; // / 8; off = x & 7; // % 8; bit = 128>>off; val = buf[lineOffsetOutput + b]; if (pixel < blackThresholdAbs) // dark { val |= bit; // set bit to one: black } else // bright { val &= (~bit); // set bit to zero: white } buf[lineOffsetOutput+b] = val; } lineOffsetOutput += bytesPerLine; } } else if (type == PPM) // maybe convert to color { outputSize = image->width * image->height * 3; if (image->color) // color already { buf = image->buffer; } else // convert to color { buf = (unsigned char*)malloc(outputSize); inputSize = image->width * image->height; offsetOutput = 0; for (offsetInput = 0; offsetInput < inputSize; offsetInput++) { pixel = image->buffer[offsetInput]; buf[offsetOutput++] = pixel; buf[offsetOutput++] = pixel; buf[offsetOutput++] = pixel; } } } else // PGM { outputSize = image->width * image->height; buf = image->buffer; } switch (type) { case PBM: outputMagic = "P4"; break; case PPM: outputMagic = "P6"; break; default: // PGM outputMagic = "P5"; break; } // write to file if ( overwrite || ( ! fileExists( filename ) ) ) { outputFile = fopen(filename, "wb"); if (outputFile != 0) { fprintf(outputFile, "%s\n", outputMagic); fprintf(outputFile, "# generated by unpaper\n"); fprintf(outputFile, "%u %u\n", image->width, image->height); if ((type == PGM)||(type == PPM)) { fprintf(outputFile, "255\n"); // maximum color index per color-component } fwrite(buf, 1, outputSize, outputFile); fclose(outputFile); } else { printf("*** error: Cannot open output file '%s'.\n", filename); result = FALSE; } } else { printf("file %s already exists (use --overwrite to replace).\n", filename); result = FALSE; } if (buf != image->buffer) { free(buf); } return result; } /** * Saves the image if full debugging mode is enabled. */ void saveDebug(const char* filename, struct IMAGE* image) { int type; if (verbose >= VERBOSE_DEBUG_SAVE) { if (image->color) { type = PPM; } else if (image->bitdepth == 1) { type = PBM; } else { type = PGM; } saveImage(filename, image, type, TRUE, 0.5); // 0.5 is a dummy, not used because PGM depth } } /**************************************************************************** * image processing functions * ****************************************************************************/ /* --- deskewing ---------------------------------------------------------- */ /** * Returns the maximum peak value that occurs when shifting a rotated virtual line above the image, * starting from one edge of an area and moving towards the middle point of the area. * The peak value is calulated by the absolute difference in the average blackness of pixels that occurs between two single shifting steps. * * @param m ascending slope of the virtually shifted (m=tan(angle)). Mind that this is negative for negative radians. */ int detectEdgeRotationPeak(double m, int deskewScanSize, float deskewScanDepth, int shiftX, int shiftY, int left, int top, int right, int bottom, struct IMAGE* image) { int width; int height; int mid; int half; int sideOffset; int outerOffset; double X; // unrounded coordinates double Y; double stepX; double stepY; int x[MAX_ROTATION_SCAN_SIZE]; int y[MAX_ROTATION_SCAN_SIZE]; int xx; int yy; int lineStep; int dep; int pixel; int blackness; int lastBlackness; int diff; int maxDiff; int maxBlacknessAbs; int maxDepth; int accumulatedBlackness; width = right-left+1; height = bottom-top+1; maxBlacknessAbs = (int) 255 * deskewScanSize * deskewScanDepth; if (shiftY==0) // horizontal detection { if (deskewScanSize == -1) { deskewScanSize = height; } limit(&deskewScanSize, MAX_ROTATION_SCAN_SIZE); limit(&deskewScanSize, height); maxDepth = width/2; half = deskewScanSize/2; outerOffset = (int)(abs(m) * half); mid = height/2; sideOffset = shiftX > 0 ? left-outerOffset : right+outerOffset; X = sideOffset + half * m; Y = top + mid - half; stepX = -m; stepY = 1.0; } else // vertical detection { if (deskewScanSize == -1) { deskewScanSize = width; } limit(&deskewScanSize, MAX_ROTATION_SCAN_SIZE); limit(&deskewScanSize, width); maxDepth = height/2; half = deskewScanSize/2; outerOffset = (int)(abs(m) * half); mid = width/2; sideOffset = shiftY > 0 ? top-outerOffset : bottom+outerOffset; X = left + mid - half; Y = sideOffset - (half * m); stepX = 1.0; stepY = -m; // (line goes upwards for negative degrees) } // fill buffer with coordinates for rotated line in first unshifted position for (lineStep = 0; lineStep < deskewScanSize; lineStep++) { x[lineStep] = (int)X; y[lineStep] = (int)Y; X += stepX; Y += stepY; } // now scan for edge, modify coordinates in buffer to shift line into search direction (towards the middle point of the area) // stop either when detectMaxDepth steps are shifted, or when diff falls back to less than detectThreshold*maxDiff lastBlackness = 0; diff = 0; maxDiff = 0; accumulatedBlackness = 0; for (dep = 0; (accumulatedBlackness < maxBlacknessAbs) && (dep < maxDepth) ; dep++) { // calculate blackness of virtual line blackness = 0; for (lineStep = 0; lineStep < deskewScanSize; lineStep++) { xx = x[lineStep]; x[lineStep] += shiftX; yy = y[lineStep]; y[lineStep] += shiftY; if ((xx >= left) && (xx <= right) && (yy >= top) && (yy <= bottom)) { pixel = getPixelDarknessInverse(xx, yy, image); blackness += (255 - pixel); } } diff = blackness - lastBlackness; lastBlackness = blackness; if (diff >= maxDiff) { maxDiff = diff; } accumulatedBlackness += blackness; } if (dep < maxDepth) // has not terminated only because middle was reached { return maxDiff; } else { return 0; } } /** * Detects rotation at one edge of the area specified by left, top, right, bottom. * Which of the four edges to take depends on whether shiftX or shiftY is non-zero, * and what sign this shifting value has. */ double detectEdgeRotation(float deskewScanRange, float deskewScanStep, int deskewScanSize, float deskewScanDepth, int shiftX, int shiftY, int left, int top, int right, int bottom, struct IMAGE* image) { // either shiftX or shiftY is 0, the other value is -i|+i // depending on shiftX/shiftY the start edge for shifting is determined double rangeRad; double stepRad; double rotation; int peak; int maxPeak; double detectedRotation; double m; rangeRad = degreesToRadians((double)deskewScanRange); stepRad = degreesToRadians((double)deskewScanStep); detectedRotation = 0.0; maxPeak = 0; // iteratively increase test angle, alterating between +/- sign while increasing absolute value for (rotation = 0.0; rotation <= rangeRad; rotation = (rotation>=0.0) ? -(rotation + stepRad) : -rotation ) { m = tan(rotation); peak = detectEdgeRotationPeak(m, deskewScanSize, deskewScanDepth, shiftX, shiftY, left, top, right, bottom, image); if (peak > maxPeak) { detectedRotation = rotation; maxPeak = peak; } } return radiansToDegrees(detectedRotation); } /** * Detect rotation of a whole area. * Angles between -deskewScanRange and +deskewScanRange are scanned, at either the * horizontal or vertical edges of the area specified by left, top, right, bottom. */ double detectRotation(int deskewScanEdges, int deskewScanRange, float deskewScanStep, int deskewScanSize, float deskewScanDepth, float deskewScanDeviation, int left, int top, int right, int bottom, struct IMAGE* image) { double rotation[4]; int count; double total; double average; double deviation; int i; count = 0; if ((deskewScanEdges & 1<width; h = source->height; halfX = (w-1)/2; halfY = (h-1)/2; midX = w/2; midY = h/2; midMax = max(midX, midY); // create 2D rotation matrix sinval = sin(radians); // no use of sincos()-function for compatibility, no performace bottleneck anymore anyway cosval = cos(radians); m11 = cosval; m12 = sinval; m21 = -sinval; m22 = cosval; // step through all pixels of the target image, // symmetrically in all four quadrants to reduce trigonometric calculations for (dY = 0; dY <= midMax; dY++) { for (dX = 0; dX <= midMax; dX++) { // matrix multiplication to get rotated pixel pos (as in quadrant I) diffX = dX * m11 + dY * m21; diffY = dX * m12 + dY * m22; // quadrant I x = midX + dX; y = midY - dY; if ((x < w) && (y >= 0)) { oldX = midX + diffX; oldY = midY - diffY; pixel = getPixel(oldX, oldY, source); setPixel(pixel, x, y, target); } // quadrant II x = halfX - dY; y = midY - dX; if ((x >=0) && (y >= 0)) { oldX = halfX - diffY; oldY = midY - diffX; pixel = getPixel(oldX, oldY, source); setPixel(pixel, x, y, target); } // quadrant III x = halfX - dX; y = halfY + dY; if ((x >=0) && (y < h)) { oldX = halfX - diffX; oldY = halfY + diffY; pixel = getPixel(oldX, oldY, source); setPixel(pixel, x, y, target); } // quadrant IV x = midX + dY; y = halfY + dX; if ((x < w) && (y < h)) { oldX = midX + diffY; oldY = halfY + diffX; pixel = getPixel(oldX, oldY, source); setPixel(pixel, x, y, target); } } } } /** * Converts an image buffer to a qpixel-representation, i.e. enlarge the whole * whole image both horizontally and vertically by factor 2 (leading to a * factor 4 increase in total). * qpixelBuf must have been allocated before with 4-times amount of memory as * buf. */ void convertToQPixels(struct IMAGE* image, struct IMAGE* qpixelImage) { int x; int y; int xx; int yy; int pixel; yy = 0; for (y = 0; y < image->height; y++) { xx = 0; for (x = 0; x < image->width; x++) { pixel = getPixel(x, y, image); setPixel(pixel, xx, yy, qpixelImage); setPixel(pixel, xx+1, yy, qpixelImage); setPixel(pixel, xx, yy+1, qpixelImage); setPixel(pixel, xx+1, yy+1, qpixelImage); xx += 2; } yy += 2; } } /** * Converts an image buffer back from a qpixel-representation to normal, i.e. * shrinks the whole image both horizontally and vertically by factor 2 * (leading to a factor 4 decrease in total). * buf must have been allocated before with 1/4-times amount of memory as * qpixelBuf. */ void convertFromQPixels(struct IMAGE* qpixelImage, struct IMAGE* image) { int x; int y; int xx; int yy; int pixel; int a,b,c,d; int r, g, bl; yy = 0; for (y = 0; y < image->height; y++) { xx = 0; for (x = 0; x < image->width; x++) { a = getPixel(xx, yy, qpixelImage); b = getPixel(xx+1, yy, qpixelImage); c = getPixel(xx, yy+1, qpixelImage); d = getPixel(xx+1, yy+1, qpixelImage); r = (red(a) + red(b) + red(c) + red(d)) / 4; g = (green(a) + green(b) + green(c) + green(d)) / 4; bl = (blue(a) + blue(b) + blue(c) + blue(d)) / 4; pixel = pixelValue(r, g, bl); setPixel(pixel, x, y, image); xx += 2; } yy += 2; } } /* --- stretching / resizing / shifting ------------------------------------ */ /** * Stretches the image so that the resulting image has a new size. * * @param w the new width to stretch to * @param h the new height to stretch to */ void stretch(int w, int h, struct IMAGE* image) { struct IMAGE newimage; int x; int y; int matrixX; int matrixY; int matrixWidth; int matrixHeight; int blockWidth; int blockHeight; int blockWidthRest; int blockHeightRest; int fillIndexWidth; int fillIndexHeight; int fill; int xx; int yy; int sum; int sumR; int sumG; int sumB; int sumCount; int pixel; // allocate new buffer's memory initImage(&newimage, w, h, image->bitdepth, image->color, WHITE); blockWidth = image->width / w; // (0 if enlarging, i.e. w > image->width) blockHeight = image->height / h; if (w <= image->width) { blockWidthRest = (image->width) % w; } else // modulo-operator doesn't work as expected: (3680 % 7360)==3680 ! (not 7360 as expected) { // shouldn't always be a % b = b if a < b ? blockWidthRest = w; } if (h <= image->height) { blockHeightRest = (image->height) % h; } else { blockHeightRest = h; } // for each new pixel, get a matrix of pixels from which the new pixel should be derived // (when enlarging, this matrix is always of size 1x1) matrixY = 0; fillIndexHeight = 0; for (y = 0; y < h; y++) { fillIndexWidth = 0; matrixX = 0; if ( ( (y * blockHeightRest) / h ) == fillIndexHeight ) // next fill index? { // (If our optimizer is cool, the above "* blockHeightRest / h" will disappear // when images are enlarged, because in that case blockHeightRest = h has been set before, // thus we're in a Kripke-branch where blockHeightRest and h are the same variable. // No idea if gcc's optimizer does this...) (See again below.) fillIndexHeight++; fill = 1; } else { fill = 0; } matrixHeight = blockHeight + fill; for (x = 0; x < w; x++) { if ( ( (x * blockWidthRest) / w ) == fillIndexWidth ) // next fill index? { fillIndexWidth++; fill = 1; } else { fill = 0; } matrixWidth = blockWidth + fill; // if enlarging, map corrdinates directly if (blockWidth == 0) // enlarging { matrixX = (x * image->width) / w; } if (blockHeight == 0) // enlarging { matrixY = (y * image->height) / h; } // calculate average pixel value in source matrix if ((matrixWidth == 1) && (matrixHeight == 1)) // optimization: quick version { pixel = getPixel(matrixX, matrixY, image); } else { sumCount = 0; if (!image->color) { sum = 0; for (yy = 0; yy < matrixHeight; yy++) { for (xx = 0; xx < matrixWidth; xx++) { sum += getPixelGrayscale(matrixX + xx, matrixY + yy, image); sumCount++; } } sum = sum / sumCount; pixel = pixelGrayscaleValue(sum); } else // color { sumR = 0; sumG = 0; sumB = 0; for (yy = 0; yy < matrixHeight; yy++) { for (xx = 0; xx < matrixWidth; xx++) { pixel = getPixel(matrixX + xx, matrixY + yy, image); sumR += (pixel >> 16) & 0xff; sumG += (pixel >> 8) & 0xff; sumB += pixel & 0xff; //sumR += getPixelComponent(matrixX + xx, matrixY + yy, RED, image); //sumG += getPixelComponent(matrixX + xx, matrixY + yy, GREEN, image); //sumB += getPixelComponent(matrixX + xx, matrixY + yy, BLUE, image); sumCount++; } } pixel = pixelValue( sumR/sumCount, sumG/sumCount, sumB/sumCount ); } } setPixel(pixel, x, y, &newimage); // pixel may have resulted in a gray value, which will be converted to 1-bit // when the file gets saved, if .pbm format requested. black-threshold will apply. if (blockWidth > 0) // shrinking { matrixX += matrixWidth; } } if (blockHeight > 0) // shrinking { matrixY += matrixHeight; } } replaceImage(image, &newimage); } /** * Resizes the image so that the resulting sheet has a new size and the image * content is zoomed to fit best into the sheet, while keeping it's aspect ration. * * @param w the new width to resize to * @param h the new height to resize to */ void resize(int w, int h, struct IMAGE* image) { struct IMAGE newimage; int ww; int hh; float wRat; float hRat; wRat = (float)w / image->width; hRat = (float)h / image->height; if (wRat < hRat) // horizontally more shrinking/less enlarging is needed: fill width fully, adjust height { ww = w; hh = image->height * w / image->width; } else if (hRat < wRat) { ww = image->width * h / image->height; hh = h; } else // wRat == hRat { ww = w; hh = h; } stretch(ww, hh, image); initImage(&newimage, w, h, image->bitdepth, image->color, image->background); centerImage(image, 0, 0, w, h, &newimage); replaceImage(image, &newimage); } /** * Shifts the image. * * @param shiftX horizontal shifting * @param shiftY vertical shifting */ void shift(int shiftX, int shiftY, struct IMAGE* image) { struct IMAGE newimage; int x; int y; int pixel; // allocate new buffer's memory initImage(&newimage, image->width, image->height, image->bitdepth, image->color, image->background); for (y = 0; y < image->height; y++) { for (x = 0; x < image->width; x++) { pixel = getPixel(x, y, image); setPixel(pixel, x + shiftX, y + shiftY, &newimage); } } replaceImage(image, &newimage); } /* --- mask-detection ----------------------------------------------------- */ /** * Finds one edge of non-black pixels headig from one starting point towards edge direction. * * @return number of shift-steps until blank edge found */ int detectEdge(int startX, int startY, int shiftX, int shiftY, int maskScanSize, int maskScanDepth, float maskScanThreshold, struct IMAGE* image) { // either shiftX or shiftY is 0, the other value is -i|+i int left; int top; int right; int bottom; int half; int halfDepth; int blackness; int total; int count; half = maskScanSize / 2; total = 0; count = 0; if (shiftY==0) // vertical border is to be detected, horizontal shifting of scan-bar { if (maskScanDepth == -1) { maskScanDepth = image->height; } halfDepth = maskScanDepth / 2; left = startX - half; top = startY - halfDepth; right = startX + half; bottom = startY + halfDepth; } else // horizontal border is to be detected, vertical shifting of scan-bar { if (maskScanDepth == -1) { maskScanDepth = image->width; } halfDepth = maskScanDepth / 2; left = startX - halfDepth; top = startY - half; right = startX + halfDepth; bottom = startY + half; } while (TRUE) // ! { blackness = 255 - brightnessRect(left, top, right, bottom, image); total += blackness; count++; // is blackness below threshold*average? if ((blackness < ((maskScanThreshold*total)/count))||(blackness==0)) // this will surely become true when pos reaches the outside of the actual image area and blacknessRect() will deliver 0 because all pixels outside are considered white { return count; // ! return here, return absolute value of shifting difference } left += shiftX; right += shiftX; top += shiftY; bottom += shiftY; } } /** * Detects a mask of white borders around a starting point. * The result is returned via call-by-reference parameters left, top, right, bottom. * * @return the detected mask in left, top, right, bottom; or -1, -1, -1, -1 if no mask could be detected */ BOOLEAN detectMask(int startX, int startY, int maskScanDirections, int maskScanSize[DIRECTIONS_COUNT], int maskScanDepth[DIRECTIONS_COUNT], int maskScanStep[DIRECTIONS_COUNT], float maskScanThreshold[DIRECTIONS_COUNT], int maskScanMinimum[DIMENSIONS_COUNT], int maskScanMaximum[DIMENSIONS_COUNT], int* left, int* top, int* right, int* bottom, struct IMAGE* image) { int width; int height; int half[DIRECTIONS_COUNT]; BOOLEAN success; half[HORIZONTAL] = maskScanSize[HORIZONTAL] / 2; half[VERTICAL] = maskScanSize[VERTICAL] / 2; if ((maskScanDirections & 1<width - 1; } if ((maskScanDirections & 1<height - 1; } // if below minimum or above maximum, set to maximum width = *right - *left; height = *bottom - *top; success = TRUE; if ( ((maskScanMinimum[WIDTH] != -1) && (width < maskScanMinimum[WIDTH])) || ((maskScanMaximum[WIDTH] != -1) && (width > maskScanMaximum[WIDTH])) ) { width = maskScanMaximum[WIDTH] / 2; *left = startX - width; *right = startX + width; success = FALSE;; } if ( ((maskScanMinimum[HEIGHT] != -1) && (height < maskScanMinimum[HEIGHT])) || ((maskScanMaximum[HEIGHT] != -1) && (height > maskScanMaximum[HEIGHT])) ) { height = maskScanMaximum[HEIGHT] / 2; *top = startY - height; *bottom = startY + height; success = FALSE; } return success; } /** * Detects masks around the points specified in point[]. * * @param mask point to array into which detected masks will be stored * @return number of masks stored in mask[][] */ int detectMasks(int mask[MAX_MASKS][EDGES_COUNT], BOOLEAN maskValid[MAX_MASKS], int point[MAX_POINTS][COORDINATES_COUNT], int pointCount, int maskScanDirections, int maskScanSize[DIRECTIONS_COUNT], int maskScanDepth[DIRECTIONS_COUNT], int maskScanStep[DIRECTIONS_COUNT], float maskScanThreshold[DIRECTIONS_COUNT], int maskScanMinimum[DIMENSIONS_COUNT], int maskScanMaximum[DIMENSIONS_COUNT], struct IMAGE* image) { int left; int top; int right; int bottom; int i; int maskCount; maskCount = 0; if (maskScanDirections != 0) { for (i = 0; i < pointCount; i++) { maskValid[i] = detectMask(point[i][X], point[i][Y], maskScanDirections, maskScanSize, maskScanDepth, maskScanStep, maskScanThreshold, maskScanMinimum, maskScanMaximum, &left, &top, &right, &bottom, image); if (!(left==-1 || top==-1 || right==-1 || bottom==-1)) { mask[maskCount][LEFT] = left; mask[maskCount][TOP] = top; mask[maskCount][RIGHT] = right; mask[maskCount][BOTTOM] = bottom; maskCount++; } //if (maskValid[i] == FALSE) { // (mask had been auto-set to full page size) // if (verbose>=VERBOSE_NORMAL) { // printf("auto-masking (%d,%d): NO MASK DETECTED\n", point[i][X], point[i][Y]); // } //} } } return maskCount; } /** * Permanently applies image masks. Each pixel which is not covered by at least * one mask is set to maskColor. */ void applyMasks(int mask[MAX_MASKS][EDGES_COUNT], int maskCount, int maskColor, struct IMAGE* image) { int x; int y; int i; int left, top, right, bottom; BOOLEAN m; if (maskCount<=0) { return; } for (y=0; y < image->height; y++) { for (x=0; x < image->width; x++) { // in any mask? m = FALSE; for (i=0; ((m==FALSE) && (i=top && y<=bottom && x>=left && x<=right) { m = TRUE; } } if (m == FALSE) { setPixel(maskColor, x, y, image); // delete: set to white } } } } /* --- wiping ------------------------------------------------------------- */ /** * Permanently wipes out areas of an images. Each pixel covered by a wipe-area * is set to wipeColor. */ void applyWipes(int area[MAX_MASKS][EDGES_COUNT], int areaCount, int wipeColor, struct IMAGE* image) { int x; int y; int i; int count; for (i = 0; i < areaCount; i++) { count = 0; for (y = area[i][TOP]; y <= area[i][BOTTOM]; y++) { for (x = area[i][LEFT]; x <= area[i][RIGHT]; x++) { if ( setPixel(wipeColor, x, y, image) ) { count++; } } } } } /* --- mirroring ---------------------------------------------------------- */ /** * Mirrors an image either horizontally, vertically, or both. */ void mirror(int directions, struct IMAGE* image) { int x; int y; int xx; int yy; int pixel1; int pixel2; BOOLEAN horizontal; BOOLEAN vertical; int untilX; int untilY; horizontal = ((directions & 1<width - 1) >> 1) : (image->width - 1); // w>>1 == (int)(w-0.5)/2 untilY = (vertical==TRUE) ? ((image->height - 1) >> 1) : image->height - 1; for (y = 0; y <= untilY; y++) { yy = (vertical==TRUE) ? (image->height - y - 1) : y; if ((vertical==TRUE) && (horizontal==TRUE) && (y == yy)) // last middle line in odd-lined image mirrored both h and v { untilX = ((image->width - 1) >> 1); } for (x = 0; x <= untilX; x++) { xx = (horizontal==TRUE) ? (image->width - x - 1) : x; pixel1 = getPixel(x, y, image); pixel2 = getPixel(xx, yy, image); setPixel(pixel2, x, y, image); setPixel(pixel1, xx, yy, image); } } } /* --- flip-rotating ------------------------------------------------------ */ /** * Rotates an image clockwise or anti-clockwise in 90-degrees. * * @param direction either -1 (rotate anti-clockwise) or 1 (rotate clockwise) */ void flipRotate(int direction, struct IMAGE* image) { struct IMAGE newimage; int x; int y; int xx; int yy; int pixel; initImage(&newimage, image->height, image->width, image->bitdepth, image->color, WHITE); // exchanged width and height for (y = 0; y < image->height; y++) { xx = ((direction > 0) ? image->height - 1 : 0) - y * direction; for (x = 0; x < image->width; x++) { yy = ((direction < 0) ? image->width - 1 : 0) + x*direction; pixel = getPixel(x, y, image); setPixel(pixel, xx, yy, &newimage); } } replaceImage(image, &newimage); } /* --- blackfilter -------------------------------------------------------- */ /** * Filters out solidly black areas scanning to one direction. * * @param stepX is 0 if stepY!=0 * @param stepY is 0 if stepX!=0 * @see blackfilter() */ void blackfilterScan(int stepX, int stepY, int size, int dep, float threshold, int exclude[MAX_MASKS][EDGES_COUNT], int excludeCount, int intensity, float blackThreshold, struct IMAGE* image) { int left; int top; int right; int bottom; int blackness; int thresholdBlack; int x; int y; int shiftX; int shiftY; int l, t, r, b; int total; int diffX; int diffY; int mask[EDGES_COUNT]; BOOLEAN alreadyExcludedMessage; thresholdBlack = (int)(WHITE * (1.0-blackThreshold)); total = size * dep; if (stepX != 0) // horizontal scanning { left = 0; top = 0; right = size -1; bottom = dep - 1; shiftX = 0; shiftY = dep; } else // vertical scanning { left = 0; top = 0; right = dep -1; bottom = size - 1; shiftX = dep; shiftY = 0; } while ((left < image->width) && (top < image->height)) // individual scanning "stripes" over the whole sheet { l = left; t = top; r = right; b = bottom; // make sure last stripe does not reach outside sheet, shift back inside (next +=shift will exit while-loop) if (r >= image->width || b >= image->height) { diffX = r-image->width+1; diffY = b-image->height+1; l -= diffX; t -= diffY; r -= diffX; b -= diffY; } alreadyExcludedMessage = FALSE; while ((l < image->width) && (t < image->height)) // single scanning "stripe" { blackness = 255 - darknessInverseRect(l, t, r, b, image); if (blackness >= 255*threshold) // found a solidly black area { mask[LEFT] = l; mask[TOP] = t; mask[RIGHT] = r; mask[BOTTOM] = b; if (! masksOverlapAny(mask, exclude, excludeCount) ) { // start flood-fill in this area (on each pixel to make sure we get everything, in most cases first flood-fill from first pixel will delete all other black pixels in the area already) for (y = t; y <= b; y++) { for (x = l; x <= r; x++) { floodFill(x, y, pixelValue(WHITE, WHITE, WHITE), 0, thresholdBlack, intensity, image); } } } } l += stepX; t += stepY; r += stepX; b += stepY; } left += shiftX; top += shiftY; right += shiftX; bottom += shiftY; } } /** * Filters out solidly black areas, as appearing on bad photocopies. * A virtual bar of width 'size' and height 'depth' is horizontally moved * above the middle of the sheet (or the full sheet, if depth ==-1). */ void blackfilter(int blackfilterScanDirections, int blackfilterScanSize[DIRECTIONS_COUNT], int blackfilterScanDepth[DIRECTIONS_COUNT], int blackfilterScanStep[DIRECTIONS_COUNT], float blackfilterScanThreshold, int blackfilterExclude[MAX_MASKS][EDGES_COUNT], int blackfilterExcludeCount, int blackfilterIntensity, float blackThreshold, struct IMAGE* image) { if ((blackfilterScanDirections & 1<height; y++) { for (x = 0; x < image->width; x++) { pixel = getPixelDarknessInverse(x, y, image); if (pixel < whiteMin) // one dark pixel found { neighbors = countPixelNeighbors(x, y, intensity, whiteMin, image); // get number of non-light pixels in neighborhood if (neighbors <= intensity) // ...not more than 'intensity'? { clearPixelNeighbors(x, y, whiteMin, image); // delete area count++; } } } } return count; } /* --- blurfilter --------------------------------------------------------- */ /** * Removes noise using a kind of blurfilter, as alternative to the noise * filter. This algoithm counts pixels while 'shaking' the area to detect, * and clears the area if the amount of white pixels exceeds whiteTreshold. */ int blurfilter(int blurfilterScanSize[DIRECTIONS_COUNT], int blurfilterScanStep[DIRECTIONS_COUNT], float blurfilterIntensity, float whiteThreshold, struct IMAGE* image) { int whiteMin; int left; int top; int right; int bottom; int count; int max; int total; int result; result = 0; whiteMin = (int)(WHITE * whiteThreshold); left = 0; top = 0; right = blurfilterScanSize[HORIZONTAL] - 1; bottom = blurfilterScanSize[VERTICAL] - 1; total = blurfilterScanSize[HORIZONTAL] * blurfilterScanSize[VERTICAL]; while (TRUE) // ! { max = 0; count = countPixelsRect(left, top, right, bottom, 0, whiteMin, FALSE, image); if (count > max) { max = count; } count = countPixelsRect(left-blurfilterScanStep[HORIZONTAL], top-blurfilterScanStep[VERTICAL], right-blurfilterScanStep[HORIZONTAL], bottom-blurfilterScanStep[VERTICAL], 0, whiteMin, FALSE, image); if (count > max) { max = count; } count = countPixelsRect(left+blurfilterScanStep[HORIZONTAL], top-blurfilterScanStep[VERTICAL], right+blurfilterScanStep[HORIZONTAL], bottom-blurfilterScanStep[VERTICAL], 0, whiteMin, FALSE, image); if (count > max) { max = count; } count = countPixelsRect(left-blurfilterScanStep[HORIZONTAL], top+blurfilterScanStep[VERTICAL], right-blurfilterScanStep[HORIZONTAL], bottom+blurfilterScanStep[VERTICAL], 0, whiteMin, FALSE, image); if (count > max) { max = count; } count = countPixelsRect(left+blurfilterScanStep[HORIZONTAL], top+blurfilterScanStep[VERTICAL], right+blurfilterScanStep[HORIZONTAL], bottom+blurfilterScanStep[VERTICAL], 0, whiteMin, FALSE, image); if (count > max) { max = count; } if ((((float)max)/total) <= blurfilterIntensity) { result += countPixelsRect(left, top, right, bottom, 0, whiteMin, TRUE, image); // also clear } if (right < image->width) // not yet at end of row { left += blurfilterScanStep[HORIZONTAL]; right += blurfilterScanStep[HORIZONTAL]; } else // end of row { if (bottom >= image->height) // has been last row { return result; // exit here } // next row: left = 0; right = blurfilterScanSize[HORIZONTAL] - 1; top += blurfilterScanStep[VERTICAL]; bottom += blurfilterScanStep[VERTICAL]; } } } /* --- grayfilter --------------------------------------------------------- */ /** * Clears areas which do not contain any black pixels, but some "gray shade" only. * Two conditions have to apply before an area gets deleted: first, not a single black pixel may be contained, * second, a minimum threshold of blackness must not be exceeded. */ int grayfilter(int grayfilterScanSize[DIRECTIONS_COUNT], int grayfilterScanStep[DIRECTIONS_COUNT], float grayfilterThreshold, float blackThreshold, struct IMAGE* image) { int blackMax; int left; int top; int right; int bottom; int count; int lightness; int thresholdAbs; int total; int result; result = 0; blackMax = (int)(WHITE * (1.0-blackThreshold)); thresholdAbs = (int)(WHITE * grayfilterThreshold); left = 0; top = 0; right = grayfilterScanSize[HORIZONTAL] - 1; bottom = grayfilterScanSize[VERTICAL] - 1; total = grayfilterScanSize[HORIZONTAL] * grayfilterScanSize[VERTICAL]; while (TRUE) // ! { count = countPixelsRect(left, top, right, bottom, 0, blackMax, FALSE, image); if (count == 0) { lightness = lightnessRect(left, top, right, bottom, image); if ((WHITE - lightness) < thresholdAbs) // (lower threshold->more deletion) { result += clearRect(left, top, right, bottom, image, WHITE); } } if (left < image->width) // not yet at end of row { left += grayfilterScanStep[HORIZONTAL]; right += grayfilterScanStep[HORIZONTAL]; } else // end of row { if (bottom >= image->height) // has been last row { return result; // exit here } // next row: left = 0; right = grayfilterScanSize[HORIZONTAL] - 1; top += grayfilterScanStep[VERTICAL]; bottom += grayfilterScanStep[VERTICAL]; } } } /* --- border-detection --------------------------------------------------- */ /** * Moves a rectangular area of pixels to be centered above the centerX, centerY coordinates. */ void centerMask(int centerX, int centerY, int left, int top, int right, int bottom, struct IMAGE* image, int &dx, int &dy) { struct IMAGE newimage; int width; int height; int targetX; int targetY; width = right - left + 1; height = bottom - top + 1; targetX = centerX - width/2; targetY = centerY - height/2; // printf("centerMask %d %d\n",targetX-left,targetY-top); dx = targetX-left; dy = targetY-top; if ((targetX >= 0) && (targetY >= 0) && ((targetX+width) <= image->width) && ((targetY+height) <= image->height)) { initImage(&newimage, width, height, image->bitdepth, image->color, image->background); copyImageArea(left, top, width, height, image, 0, 0, &newimage); clearRect(left, top, right, bottom, image, image->background); copyImageArea(0, 0, width, height, &newimage, targetX, targetY, image); freeImage(&newimage); } } /** * Moves a rectangular area of pixels to be centered inside a specified area coordinates. */ void alignMask(int mask[EDGES_COUNT], int outside[EDGES_COUNT], int direction, int margin[DIRECTIONS_COUNT], struct IMAGE* image, int &dx, int &dy) { struct IMAGE newimage; int width; int height; int targetX; int targetY; width = mask[RIGHT] - mask[LEFT] + 1; height = mask[BOTTOM] - mask[TOP] + 1; if (direction & 1<bitdepth, image->color, image->background); copyImageArea(mask[LEFT], mask[TOP], mask[RIGHT], mask[BOTTOM], image, 0, 0, &newimage); clearRect(mask[LEFT], mask[TOP], mask[RIGHT], mask[BOTTOM], image, image->background); copyImageArea(0, 0, width, height, &newimage, targetX, targetY, image); freeImage(&newimage); } /** * Find the size of one border edge. * * @param x1..y2 area inside of which border is to be detected */ int detectBorderEdge(int outsideMask[EDGES_COUNT], int stepX, int stepY, int size, int threshold, int maxBlack, struct IMAGE* image) { int left; int top; int right; int bottom; int max; int cnt; int result; if (stepY == 0) // horizontal detection { if (stepX > 0) { left = outsideMask[LEFT]; top = outsideMask[TOP]; right = outsideMask[LEFT] + size; bottom = outsideMask[BOTTOM]; } else { left = outsideMask[RIGHT] - size; top = outsideMask[TOP]; right = outsideMask[RIGHT]; bottom = outsideMask[BOTTOM]; } max = (outsideMask[RIGHT] - outsideMask[LEFT]); } else // vertical detection { if (stepY > 0) { left = outsideMask[LEFT]; top = outsideMask[TOP]; right = outsideMask[RIGHT]; bottom = outsideMask[TOP] + size; } else { left = outsideMask[LEFT]; top = outsideMask[BOTTOM] - size; right = outsideMask[RIGHT]; bottom = outsideMask[BOTTOM]; } max = (outsideMask[BOTTOM] - outsideMask[TOP]); } result = 0; while (result < max) { cnt = countPixelsRect(left, top, right, bottom, 0, maxBlack, FALSE, image); if (cnt >= threshold) { return result; // border has been found: regular exit here } left += stepX; top += stepY; right += stepX; bottom += stepY; result += abs(stepX+stepY); // (either stepX or stepY is 0) } return 0; // no border found between 0..max } /** * Detects a border of completely non-black pixels around the area outsideBorder[LEFT],outsideBorder[TOP]-outsideBorder[RIGHT],outsideBorder[BOTTOM]. */ void detectBorder(int border[EDGES_COUNT], int borderScanDirections, int borderScanSize[DIRECTIONS_COUNT], int borderScanStep[DIRECTIONS_COUNT], int borderScanThreshold[DIRECTIONS_COUNT], float blackThreshold, int outsideMask[EDGES_COUNT], struct IMAGE* image) { int blackThresholdAbs; border[LEFT] = outsideMask[LEFT]; border[TOP] = outsideMask[TOP]; border[RIGHT] = image->width - outsideMask[RIGHT]; border[BOTTOM] = image->height - outsideMask[BOTTOM]; blackThresholdAbs = (int)(WHITE * (1.0 - blackThreshold)); if (borderScanDirections & 1<width - border[RIGHT] - 1; mask[BOTTOM] = image->height - border[BOTTOM] - 1; } /** * Applies a border to the whole image. All pixels in the border range at the * edges of the sheet will be cleared. */ void applyBorder(int border[EDGES_COUNT], int borderColor, struct IMAGE* image) { int mask[EDGES_COUNT]; if (border[LEFT]!=0 || border[TOP]!=0 || border[RIGHT]!=0 || border[BOTTOM]!=0) { borderToMask(border, mask, image); applyMasks(&mask, 1, borderColor, image); } } inline void fromImageToStruct(const Magick::Image &source, struct IMAGE* image, int* type) { *type = PGM; image->bitdepth = 8; image->color = FALSE; image->width = source.columns(); image->height = source.rows(); int bytesPerLine = image->width; int inputSize = bytesPerLine * image->height; image->buffer = (unsigned char*) malloc(inputSize); Magick::ColorGray c; for (int j = 0; j < image->height; j++) for (int i = 0; i < image->width; i++) { c = source.pixelColor(i, j); image->buffer[j * bytesPerLine + i] = (unsigned char) (255 * c.shade()); } image->bufferGrayscale = image->buffer; image->bufferLightness = image->buffer; image->bufferDarknessInverse = image->buffer; } inline void fromStructToImage(Magick::Image &target, struct IMAGE* image) { int bytesPerLine = image->width; Magick::ColorGray c; target.modifyImage(); target.erase(); for (int j = 0; j < image->height; j++) for (int i = 0; i < image->width; i++) { //printf("%d ", image->buffer[j * bytesPerLine + i]); c.shade((1. * image->buffer[j * bytesPerLine + i]) / 255); target.pixelColor(i, j, c); } //target.write("debug.png"); } /**************************************************************************** * MAIN() * ****************************************************************************/ /** * The main program. */ int unpaper(Magick::Image &picture, double &radians, int &unpaper_dx, int &unpaper_dy) { // --- parameter variables --- int layout; int startSheet; int endSheet; int startInput; int startOutput; int inputCount; int outputCount; const char* inputFileSequence[MAX_FILES]; int inputFileSequenceCount; const char* outputFileSequence[MAX_FILES]; int outputFileSequenceCount; int sheetSize[DIMENSIONS_COUNT]; int sheetBackground; int preRotate; int postRotate; int preMirror; int postMirror; int preShift[DIRECTIONS_COUNT]; int postShift[DIRECTIONS_COUNT]; int size[DIRECTIONS_COUNT]; int postSize[DIRECTIONS_COUNT]; int stretchSize[DIRECTIONS_COUNT]; int postStretchSize[DIRECTIONS_COUNT]; float zoomFactor; float postZoomFactor; int pointCount; int point[MAX_POINTS][COORDINATES_COUNT]; int maskCount; int mask[MAX_MASKS][EDGES_COUNT]; int wipeCount; int wipe[MAX_MASKS][EDGES_COUNT]; int middleWipe[2]; int preWipeCount; int preWipe[MAX_MASKS][EDGES_COUNT]; int postWipeCount; int postWipe[MAX_MASKS][EDGES_COUNT]; int preBorder[EDGES_COUNT]; int postBorder[EDGES_COUNT]; int border[EDGES_COUNT]; BOOLEAN maskValid[MAX_MASKS]; int preMaskCount; int preMask[MAX_MASKS][EDGES_COUNT]; int blackfilterScanDirections; int blackfilterScanSize[DIRECTIONS_COUNT]; int blackfilterScanDepth[DIRECTIONS_COUNT]; int blackfilterScanStep[DIRECTIONS_COUNT]; float blackfilterScanThreshold; int blackfilterExcludeCount; int blackfilterExclude[MAX_MASKS][EDGES_COUNT]; int blackfilterIntensity; int noisefilterIntensity; int blurfilterScanSize[DIRECTIONS_COUNT]; int blurfilterScanStep[DIRECTIONS_COUNT]; float blurfilterIntensity; int grayfilterScanSize[DIRECTIONS_COUNT]; int grayfilterScanStep[DIRECTIONS_COUNT]; float grayfilterThreshold; int maskScanDirections; int maskScanSize[DIRECTIONS_COUNT]; int maskScanDepth[DIRECTIONS_COUNT]; int maskScanStep[DIRECTIONS_COUNT]; float maskScanThreshold[DIRECTIONS_COUNT]; int maskScanMinimum[DIMENSIONS_COUNT]; int maskScanMaximum[DIMENSIONS_COUNT]; int maskColor; int deskewScanEdges; int deskewScanSize; float deskewScanDepth; float deskewScanRange; float deskewScanStep; float deskewScanDeviation; int borderScanDirections; int borderScanSize[DIRECTIONS_COUNT]; int borderScanStep[DIRECTIONS_COUNT]; int borderScanThreshold[DIRECTIONS_COUNT]; int borderAlign; int borderAlignMargin[DIRECTIONS_COUNT]; int outsideBorderscanMask[MAX_PAGES][EDGES_COUNT]; // set by --layout int outsideBorderscanMaskCount; float whiteThreshold; float blackThreshold; BOOLEAN writeoutput; BOOLEAN qpixels; BOOLEAN multisheets; char* outputTypeName; int noBlackfilterMultiIndex[MAX_MULTI_INDEX]; int noBlackfilterMultiIndexCount; int noNoisefilterMultiIndex[MAX_MULTI_INDEX]; int noNoisefilterMultiIndexCount; int noBlurfilterMultiIndex[MAX_MULTI_INDEX]; int noBlurfilterMultiIndexCount; int noGrayfilterMultiIndex[MAX_MULTI_INDEX]; int noGrayfilterMultiIndexCount; int noMaskScanMultiIndex[MAX_MULTI_INDEX]; int noMaskScanMultiIndexCount; int noMaskCenterMultiIndex[MAX_MULTI_INDEX]; int noMaskCenterMultiIndexCount; int noDeskewMultiIndex[MAX_MULTI_INDEX]; int noDeskewMultiIndexCount; int noWipeMultiIndex[MAX_MULTI_INDEX]; int noWipeMultiIndexCount; int noBorderMultiIndex[MAX_MULTI_INDEX]; int noBorderMultiIndexCount; int noBorderScanMultiIndex[MAX_MULTI_INDEX]; int noBorderScanMultiIndexCount; int noBorderAlignMultiIndex[MAX_MULTI_INDEX]; int noBorderAlignMultiIndexCount; int sheetMultiIndex[MAX_MULTI_INDEX]; int sheetMultiIndexCount; int excludeMultiIndex[MAX_MULTI_INDEX]; int excludeMultiIndexCount; int ignoreMultiIndex[MAX_MULTI_INDEX]; int ignoreMultiIndexCount; int autoborder[MAX_MASKS][EDGES_COUNT]; int autoborderMask[MAX_MASKS][EDGES_COUNT]; int insertBlankCount; int replaceBlankCount; BOOLEAN overwrite; BOOLEAN showTime; int dpi; // --- local variables --- int w; int h; int i; int j = 0; int previousWidth; int previousHeight; int previousBitdepth; BOOLEAN previousColor; int inputFileSequencePos; // index 'looping' through input-file-seq (without additionally inserted blank images) int outputFileSequencePos; // index 'looping' through output-file-seq int inputFileSequencePosTotal; // index 'looping' through input-file-seq (with additional blank images) struct IMAGE sheet; struct IMAGE sheetBackup; struct IMAGE originalSheet; struct IMAGE qpixelSheet; struct IMAGE page; const char* layoutStr; char* inputTypeName; char* inputTypeNames[MAX_PAGES]; int inputType; int filterResult; double rotation; int q; struct IMAGE rect; struct IMAGE rectTarget; int outputType; int outputDepth; int bd; BOOLEAN col; BOOLEAN success; BOOLEAN anyWildcards; BOOLEAN allInputFilesMissing; int nr = 0; int inputNr; int outputNr; BOOLEAN first; clock_t startTime; clock_t endTime; clock_t time; unsigned long int totalTime; int totalCount; int blankCount; int exitCode; sheet.buffer = NULL; page.buffer = NULL; exitCode = 0; // error code to return bd = 1; // default bitdepth if not resolvable (i.e. usually empty input, so bd=1 is good choice) col = FALSE; // default no color if not resolvable // explicitly un-initialize variables that are sometimes not used to avoid compiler warnings qpixelSheet.buffer = NULL; // used optionally, deactivated by --no-qpixels startTime = 0; // used optionally in debug mode -vv or with --time endTime = 0; // used optionally in debug mode -vv or with --time inputNr = -1; // will be initialized in first run of main-loop outputNr = -1; // will be initialized in first run of main-loop // ----------------------------------------------------------------------- // --- process all sheets --- // ----------------------------------------------------------------------- // count from start sheet to end sheet startSheet = 1; // defaults, may be changed in first run of for-loop endSheet = -1; startInput = -1; startOutput = -1; totalTime = 0; totalCount = 0; inputFileSequencePos = 0; outputFileSequencePos = 0; inputFileSequencePosTotal = 0; previousWidth = previousHeight = previousBitdepth = -1; previousColor = FALSE; first = TRUE; { // --- default values --- w = h = -1; layout = LAYOUT_SINGLE; layoutStr = "single"; preRotate = 0; postRotate = 0; preMirror = 0; postMirror = 0; preShift[WIDTH] = preShift[HEIGHT] = 0; postShift[WIDTH] = postShift[HEIGHT] = 0; size[WIDTH] = size[HEIGHT] = -1; postSize[WIDTH] = postSize[HEIGHT] = -1; stretchSize[WIDTH] = stretchSize[HEIGHT] = -1; postStretchSize[WIDTH] = postStretchSize[HEIGHT] = -1; zoomFactor = 1.0; postZoomFactor = 1.0; outputTypeName = NULL; // default derived from input outputDepth = -1; // default derived from input pointCount = 0; maskCount = 0; preMaskCount = 0; wipeCount = 0; preWipeCount = 0; postWipeCount = 0; middleWipe[0] = middleWipe[1] = 0; // left/right border[LEFT] = border[TOP] = border[RIGHT] = border[BOTTOM] = 0; preBorder[LEFT] = preBorder[TOP] = preBorder[RIGHT] = preBorder[BOTTOM] = 0; postBorder[LEFT] = postBorder[TOP] = postBorder[RIGHT] = postBorder[BOTTOM] = 0; blackfilterScanDirections = (1<= VERBOSE_DEBUG); // always show processing time in verbose-debug mode // get filenames inputFileSequence[0] = ""; inputFileSequenceCount = 1; outputFileSequence[0] = ""; outputFileSequenceCount = 1; // resolve filenames for current sheet anyWildcards = FALSE; allInputFilesMissing = TRUE; blankCount = 0; { // at least one input page file exists // --------------------------------------------------------------- // --- process single sheet --- // --------------------------------------------------------------- if (isInMultiIndex(nr, sheetMultiIndex, sheetMultiIndexCount) && (!isInMultiIndex(nr, excludeMultiIndex, excludeMultiIndexCount))) { // load input image(s) success = TRUE; // success = loadImage(inputFilenamesResolved[j], &page, &inputType); fromImageToStruct(picture,&page,&inputType); inputTypeName = (char*)FILETYPE_NAMES[inputType]; inputTypeNames[j] = inputTypeName; if (!success) { exitCode = 2; } else { // pre-rotate if (preRotate != 0) { if (preRotate == 90) { flipRotate(1, &page); } else if (preRotate == -90) { flipRotate(-1, &page); } } // if sheet-size is not known yet (and not forced by --sheet-size), set now based on size of (first) input image if ( w == -1 ) { if ( sheetSize[WIDTH] != -1 ) { w = sheetSize[WIDTH]; } else { w = page.width * inputCount; } } if ( h == -1 ) { if ( sheetSize[HEIGHT] != -1 ) { h = sheetSize[HEIGHT]; } else { h = page.height; } } } // place image into sheet buffer if ( (inputCount == 1) && (page.buffer != NULL) && (page.width == w) && (page.height == h) ) // quick case: single input file == whole sheet { sheet.buffer = page.buffer; sheet.bufferGrayscale = page.bufferGrayscale; sheet.bufferLightness = page.bufferLightness; sheet.bufferDarknessInverse = page.bufferDarknessInverse; sheet.width = page.width; sheet.height = page.height; sheet.bitdepth = page.bitdepth; sheet.color = page.color; sheet.background = sheetBackground; } else // generic case: place image onto sheet by copying { // allocate sheet-buffer if not done yet if ((sheet.buffer == NULL) && (w != -1) && (h != -1)) { if ((page.buffer != NULL) && (page.bitdepth != 0)) { bd = page.bitdepth; col = page.color; } else { if (outputDepth != -1) // set via --depth { bd = outputDepth; } else { // bd remains default } } initImage(&sheet, w, h, bd, col, sheetBackground); } else if ((page.buffer != NULL) && ((page.bitdepth > sheet.bitdepth) || ( (!sheet.color) && page.color ))) // make sure current sheet buffer has enough bitdepth and color-mode { sheetBackup = sheet; // re-allocate sheet bd = page.bitdepth; col = page.color; initImage(&sheet, w, h, bd, col, sheetBackground); // copy old one copyImage(&sheetBackup, 0, 0, &sheet); freeImage(&sheetBackup); } if (page.buffer != NULL) { centerImage(&page, (w * j / inputCount), 0, (w / inputCount), h, &sheet); freeImage(&page); } } // the only case that buffer is not yet initialized is if all blank pages have been inserted if (sheet.buffer == NULL) { // last chance: try to get previous (unstretched/not zoomed) sheet size w = previousWidth; h = previousHeight; bd = previousBitdepth; col = previousColor; if ((w == -1) || (h == -1)) { return 2; } else { initImage(&sheet, w, h, bd, col, sheetBackground); } } if (success) // sheet loaded successfully, size is known { previousWidth = w; previousHeight = h; previousBitdepth = bd; previousColor = col; // handle file types if (outputTypeName == NULL) // auto-set output type according to sheet format, if not explicitly set by user { if (sheet.color) { outputType = PPM; } else { if (sheet.bitdepth == 1) { outputType = PBM; } else { outputType = PGM; } } outputTypeName = (char*)FILETYPE_NAMES[outputType]; } else // parse user-setting { outputType = -1; for (i = 0; (outputType == -1) && (i < FILETYPES_COUNT); i++) { if (strcmp(outputTypeName, FILETYPE_NAMES[i])==0) { outputType = i; } } if (outputType == -1) { return 2; } } if (outputDepth == -1) // set output depth to be as input depth, if not explicitly set by user { outputDepth = sheet.bitdepth; } if (showTime) { startTime = clock(); } // pre-mirroring if (preMirror != 0) { mirror(preMirror, &sheet); } // pre-shifting if ((preShift[WIDTH] != 0) || ((preShift[HEIGHT] != 0))) { shift(preShift[WIDTH], preShift[HEIGHT], &sheet); } // pre-masking if (preMaskCount > 0) { applyMasks(preMask, preMaskCount, maskColor, &sheet); } // ------------------------------------------------------- // --- process image data --- // ------------------------------------------------------- // stretch if ((stretchSize[WIDTH] != -1) || (stretchSize[HEIGHT] != -1)) { if (stretchSize[WIDTH] != -1) { w = stretchSize[WIDTH]; } else { w = sheet.width; } if (stretchSize[HEIGHT] != -1) { h = stretchSize[HEIGHT]; } else { h = sheet.height; } stretch(w, h, &sheet); } // zoom if (zoomFactor != 1.0) { w = sheet.width * zoomFactor; h = sheet.height * zoomFactor; stretch(w, h, &sheet); } // size if ((size[WIDTH] != -1) || (size[HEIGHT] != -1)) { if (size[WIDTH] != -1) { w = size[WIDTH]; } else { w = sheet.width; } if (size[HEIGHT] != -1) { h = size[HEIGHT]; } else { h = sheet.height; } resize(w, h, &sheet); } // handle sheet layout // LAYOUT_SINGLE if (layout == LAYOUT_SINGLE) { // This affect horizontal displacement // set middle of sheet as single starting point for mask detection if (pointCount == 0) // no manual settings, use auto-values { point[pointCount][X] = sheet.width / 2; point[pointCount][Y] = sheet.height / 2; pointCount++; } if (maskScanMaximum[WIDTH] == -1) { maskScanMaximum[WIDTH] = sheet.width; } if (maskScanMaximum[HEIGHT] == -1) { maskScanMaximum[HEIGHT] = sheet.height; } // avoid inner half of the sheet to be blackfilter-detectable if (blackfilterExcludeCount == 0) // no manual settings, use auto-values { blackfilterExclude[blackfilterExcludeCount][LEFT] = sheet.width / 4; blackfilterExclude[blackfilterExcludeCount][TOP] = sheet.height / 4; blackfilterExclude[blackfilterExcludeCount][RIGHT] = sheet.width / 2 + sheet.width / 4; blackfilterExclude[blackfilterExcludeCount][BOTTOM] = sheet.height / 2 + sheet.height / 4; blackfilterExcludeCount++; } // This affects vertical displacement // set single outside border to start scanning for final border-scan if (outsideBorderscanMaskCount == 0) // no manual settings, use auto-values { outsideBorderscanMaskCount = 1; outsideBorderscanMask[0][LEFT] = 0; outsideBorderscanMask[0][RIGHT] = sheet.width - 1; outsideBorderscanMask[0][TOP] = 0; outsideBorderscanMask[0][BOTTOM] = sheet.height - 1; } // LAYOUT_DOUBLE } else if (layout == LAYOUT_DOUBLE) { // set two middle of left/right side of sheet as starting points for mask detection if (pointCount == 0) // no manual settings, use auto-values { point[pointCount][X] = sheet.width / 4; point[pointCount][Y] = sheet.height / 2; pointCount++; point[pointCount][X] = sheet.width - sheet.width / 4; point[pointCount][Y] = sheet.height / 2; pointCount++; } if (maskScanMaximum[WIDTH] == -1) { maskScanMaximum[WIDTH] = sheet.width / 2; } if (maskScanMaximum[HEIGHT] == -1) { maskScanMaximum[HEIGHT] = sheet.height; } if (middleWipe[0] > 0 || middleWipe[1] > 0) // left, right { wipe[wipeCount][LEFT] = sheet.width / 2 - middleWipe[0]; wipe[wipeCount][TOP] = 0; wipe[wipeCount][RIGHT] = sheet.width / 2 + middleWipe[1]; wipe[wipeCount][BOTTOM] = sheet.height - 1; wipeCount++; } // avoid inner half of each page to be blackfilter-detectable if (blackfilterExcludeCount == 0) // no manual settings, use auto-values { blackfilterExclude[blackfilterExcludeCount][LEFT] = sheet.width / 8; blackfilterExclude[blackfilterExcludeCount][TOP] = sheet.height / 4; blackfilterExclude[blackfilterExcludeCount][RIGHT] = sheet.width / 4 + sheet.width / 8; blackfilterExclude[blackfilterExcludeCount][BOTTOM] = sheet.height / 2 + sheet.height / 4; blackfilterExcludeCount++; blackfilterExclude[blackfilterExcludeCount][LEFT] = sheet.width / 2 + sheet.width / 8; blackfilterExclude[blackfilterExcludeCount][TOP] = sheet.height / 4; blackfilterExclude[blackfilterExcludeCount][RIGHT] = sheet.width / 2 + sheet.width / 4 + sheet.width / 8; blackfilterExclude[blackfilterExcludeCount][BOTTOM] = sheet.height / 2 + sheet.height / 4; blackfilterExcludeCount++; } // set two outside borders to start scanning for final border-scan if (outsideBorderscanMaskCount == 0) // no manual settings, use auto-values { outsideBorderscanMaskCount = 2; outsideBorderscanMask[0][LEFT] = 0; outsideBorderscanMask[0][RIGHT] = sheet.width / 2; outsideBorderscanMask[0][TOP] = 0; outsideBorderscanMask[0][BOTTOM] = sheet.height - 1; outsideBorderscanMask[1][LEFT] = sheet.width / 2; outsideBorderscanMask[1][RIGHT] = sheet.width - 1; outsideBorderscanMask[1][TOP] = 0; outsideBorderscanMask[1][BOTTOM] = sheet.height - 1; } } // if maskScanMaximum still unset (no --layout specified), set to full sheet size now if (maskScanMinimum[WIDTH] == -1) { maskScanMaximum[WIDTH] = sheet.width; } if (maskScanMinimum[HEIGHT] == -1) { maskScanMaximum[HEIGHT] = sheet.height; } // pre-wipe if (!isExcluded(nr, noWipeMultiIndex, noWipeMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyWipes(preWipe, preWipeCount, maskColor, &sheet); } // pre-border if (!isExcluded(nr, noBorderMultiIndex, noBorderMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyBorder(preBorder, maskColor, &sheet); } // black area filter if (!isExcluded(nr, noBlackfilterMultiIndex, noBlackfilterMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { saveDebug("./_before-blackfilter.pnm", &sheet); blackfilter(blackfilterScanDirections, blackfilterScanSize, blackfilterScanDepth, blackfilterScanStep, blackfilterScanThreshold, blackfilterExclude, blackfilterExcludeCount, blackfilterIntensity, blackThreshold, &sheet); saveDebug("./_after-blackfilter.pnm", &sheet); } // noise filter if (!isExcluded(nr, noNoisefilterMultiIndex, noNoisefilterMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { filterResult = noisefilter(noisefilterIntensity, whiteThreshold, &sheet); } // blur filter if (!isExcluded(nr, noBlurfilterMultiIndex, noBlurfilterMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { filterResult = blurfilter(blurfilterScanSize, blurfilterScanStep, blurfilterIntensity, whiteThreshold, &sheet); } // mask-detection if (!isExcluded(nr, noMaskScanMultiIndex, noMaskScanMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { maskCount = detectMasks(mask, maskValid, point, pointCount, maskScanDirections, maskScanSize, maskScanDepth, maskScanStep, maskScanThreshold, maskScanMinimum, maskScanMaximum, &sheet); } /* seems to remove borders when not necessary!!! // permamently apply masks if (maskCount > 0) { applyMasks(mask, maskCount, maskColor, &sheet); } */ // gray filter if (!isExcluded(nr, noGrayfilterMultiIndex, noGrayfilterMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { filterResult = grayfilter(grayfilterScanSize, grayfilterScanStep, grayfilterThreshold, blackThreshold, &sheet); } // rotation-detection if ((!isExcluded(nr, noDeskewMultiIndex, noDeskewMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount))) { originalSheet = sheet; // copy struct entries ('clone') // convert to qpixels if (qpixels==TRUE) { initImage(&qpixelSheet, sheet.width * 2, sheet.height * 2, sheet.bitdepth, sheet.color, sheetBackground); convertToQPixels(&sheet, &qpixelSheet); sheet = qpixelSheet; q = 2; // qpixel-factor for coordinates in both directions } else { q = 1; } // detect masks again, we may get more precise results now after first masking and grayfilter if (!isExcluded(nr, noMaskScanMultiIndex, noMaskScanMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { maskCount = detectMasks(mask, maskValid, point, pointCount, maskScanDirections, maskScanSize, maskScanDepth, maskScanStep, maskScanThreshold, maskScanMinimum, maskScanMaximum, &originalSheet); } // auto-deskew each mask for (i = 0; i < maskCount; i++) { // if ( maskValid[i] == TRUE ) { // point may have been invalidated if mask has not been auto-detected // for rotation detection, original buffer is used (not qpixels) saveDebug("./_before-deskew-detect.pnm", &originalSheet); radians = 0; rotation = - detectRotation(deskewScanEdges, deskewScanRange, deskewScanStep, deskewScanSize, deskewScanDepth, deskewScanDeviation, mask[i][LEFT], mask[i][TOP], mask[i][RIGHT], mask[i][BOTTOM], &originalSheet); saveDebug("./_after-deskew-detect.pnm", &originalSheet); if (rotation != 0.0) { initImage(&rect, (mask[i][RIGHT]-mask[i][LEFT]+1)*q, (mask[i][BOTTOM]-mask[i][TOP]+1)*q, sheet.bitdepth, sheet.color, sheetBackground); initImage(&rectTarget, rect.width, rect.height, sheet.bitdepth, sheet.color, sheetBackground); // copy area to rotate into rSource copyImageArea(mask[i][LEFT]*q, mask[i][TOP]*q, rect.width, rect.height, &sheet, 0, 0, &rect); radians = degreesToRadians(rotation); // rotate rotate(radians, &rect, &rectTarget); // copy result back into whole image copyImageArea(0, 0, rectTarget.width, rectTarget.height, &rectTarget, mask[i][LEFT]*q, mask[i][TOP]*q, &sheet); freeImage(&rect); freeImage(&rectTarget); } } // convert back from qpixels if (qpixels == TRUE) { convertFromQPixels(&qpixelSheet, &originalSheet); freeImage(&qpixelSheet); sheet = originalSheet; } } // auto-center masks on either single-page or double-page layout if ( (!isExcluded(nr, noMaskCenterMultiIndex, noMaskCenterMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) && (layout != LAYOUT_NONE) && (maskCount == pointCount) ) // (maskCount==pointCount to make sure all masks had correctly been detected) { // perform auto-masking again to get more precise masks after rotation if (!isExcluded(nr, noMaskScanMultiIndex, noMaskScanMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { maskCount = detectMasks(mask, maskValid, point, pointCount, maskScanDirections, maskScanSize, maskScanDepth, maskScanStep, maskScanThreshold, maskScanMinimum, maskScanMaximum, &sheet); } // center masks on the sheet, according to their page position for (i = 0; i < maskCount; i++) { int dx=0; int dy=0; centerMask(point[i][X], point[i][Y], mask[i][LEFT], mask[i][TOP], mask[i][RIGHT], mask[i][BOTTOM], &sheet, dx, dy); unpaper_dx +=dx; unpaper_dy +=dy; } } // explicit wipe if (!isExcluded(nr, noWipeMultiIndex, noWipeMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyWipes(wipe, wipeCount, maskColor, &sheet); } // explicit border if (!isExcluded(nr, noBorderMultiIndex, noBorderMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyBorder(border, maskColor, &sheet); } // border-detection if (!isExcluded(nr, noBorderScanMultiIndex, noBorderScanMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { for (i = 0; i < outsideBorderscanMaskCount; i++) { detectBorder(autoborder[i], borderScanDirections, borderScanSize, borderScanStep, borderScanThreshold, blackThreshold, outsideBorderscanMask[i], &sheet); borderToMask(autoborder[i], autoborderMask[i], &sheet); } applyMasks(autoborderMask, outsideBorderscanMaskCount, maskColor, &sheet); for (i = 0; i < outsideBorderscanMaskCount; i++) { // border-centering if (!isExcluded(nr, noBorderAlignMultiIndex, noBorderAlignMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { int dx=0; int dy=0; alignMask(autoborderMask[i], outsideBorderscanMask[i], borderAlign, borderAlignMargin, &sheet,dx,dy); unpaper_dx +=dx; unpaper_dy +=dy; } } } // post-wipe if (!isExcluded(nr, noWipeMultiIndex, noWipeMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyWipes(postWipe, postWipeCount, maskColor, &sheet); } // post-border if (!isExcluded(nr, noBorderMultiIndex, noBorderMultiIndexCount, ignoreMultiIndex, ignoreMultiIndexCount)) { applyBorder(postBorder, maskColor, &sheet); } // post-mirroring if (postMirror != 0) { mirror(postMirror, &sheet); } // post-shifting if ((postShift[WIDTH] != 0) || ((postShift[HEIGHT] != 0))) { shift(postShift[WIDTH], postShift[HEIGHT], &sheet); } // post-rotating if (postRotate != 0) { if (postRotate == 90) { flipRotate(1, &sheet); } else if (postRotate == -90) { flipRotate(-1, &sheet); } } // post-stretch if ((postStretchSize[WIDTH] != -1) || (postStretchSize[HEIGHT] != -1)) { if (postStretchSize[WIDTH] != -1) { w = postStretchSize[WIDTH]; } else { w = sheet.width; } if (postStretchSize[HEIGHT] != -1) { h = postStretchSize[HEIGHT]; } else { h = sheet.height; } stretch(w, h, &sheet); } // post-zoom if (postZoomFactor != 1.0) { w = sheet.width * postZoomFactor; h = sheet.height * postZoomFactor; stretch(w, h, &sheet); } // post-size if ((postSize[WIDTH] != -1) || (postSize[HEIGHT] != -1)) { if (postSize[WIDTH] != -1) { w = postSize[WIDTH]; } else { w = sheet.width; } if (postSize[HEIGHT] != -1) { h = postSize[HEIGHT]; } else { h = sheet.height; } resize(w, h, &sheet); } if (showTime) { endTime = clock(); } // --- write output file --- // write split pages output if (writeoutput == TRUE) { success = TRUE; page.width = sheet.width / outputCount; page.height = sheet.height; page.bitdepth = sheet.bitdepth; page.color = sheet.color; for ( j = 0; success && (j < outputCount); j++) { // get pagebuffer if ( outputCount == 1 ) { page.buffer = sheet.buffer; page.bufferGrayscale = sheet.bufferGrayscale; page.bufferLightness = sheet.bufferLightness; page.bufferDarknessInverse = sheet.bufferDarknessInverse; } else // generic case: copy page-part of sheet into own buffer { if (page.color) { page.buffer = (unsigned char*)malloc( page.width * page.height * 3 ); page.bufferGrayscale = (unsigned char*)malloc( page.width * page.height ); page.bufferLightness = (unsigned char*)malloc( page.width * page.height ); page.bufferDarknessInverse = (unsigned char*)malloc( page.width * page.height ); } else { page.buffer = (unsigned char*)malloc( page.width * page.height ); page.bufferGrayscale = page.buffer; page.bufferLightness = page.buffer; page.bufferDarknessInverse = page.buffer; } copyImageArea(page.width * j, 0, page.width, page.height, &sheet, 0, 0, &page); } //success = saveImage(outputFilenamesResolved[j], &page, outputType, overwrite, blackThreshold); fromStructToImage(picture,&page); if ( outputCount > 1 ) { freeImage(&page); } if (success == FALSE) { exitCode = 2; } } } freeImage(&sheet); sheet.buffer = NULL; if (showTime) { if (startTime > endTime) // clock overflow { endTime -= startTime; // "re-underflow" value again startTime = 0; } time = endTime - startTime; totalTime += time; totalCount++; } } } } } return exitCode; } osra-2.1.3/src/osra_common.cpp0000664000175000017500000002655414115175251014772 0ustar igorigor/****************************************************************************** OSRA: Optical Structure Recognition Application Created by Igor Filippov, 2007-2013 (igor.v.filippov@gmail.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA *****************************************************************************/ // File: osra_common.cpp // // Common functionality routines // #include // fabs(double) #include // FLT_MAX #include // std::ofstream, std::ifstream #include "osra_segment.h" #include "osra_common.h" /* return new un-initialized bitmap. NULL with errno on error */ potrace_bitmap_t *const bm_new(int w, int h) { potrace_bitmap_t *bm; int dy = (w + BM_WORDBITS - 1) / BM_WORDBITS; bm = (potrace_bitmap_t *) malloc(sizeof(potrace_bitmap_t)); if (!bm) { return NULL; } bm->w = w; bm->h = h; bm->dy = dy; bm->map = (potrace_word *) malloc(dy * h * BM_WORDSIZE); if (!bm->map) { free(bm); return NULL; } return bm; } double distance(double x1, double y1, double x2, double y2) { return (sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))); } double angle4(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double p, l1, l2, cos; p = (x1 - x2) * (x3 - x4) + (y1 - y2) * (y3 - y4); l1 = distance(x1, y1, x2, y2); l2 = distance(x4, y4, x3, y3); cos = p / (l1 * l2); return (cos); } int get_pixel(const Image &image, const ColorGray &bg, unsigned int x, unsigned int y, double THRESHOLD) { if ((x < image.columns()) && (y < image.rows())) { ColorGray c = image.pixelColor(x, y); if (fabs(c.shade() - bg.shade()) > THRESHOLD) return (1); } return (0); } void delete_curve(std::vector &atom, std::vector &bond, int n_atom, int n_bond, const potrace_path_t * const curve) { for (int i = 0; i < n_atom; i++) { if (atom[i].curve == curve) { atom[i].exists = false; } } for (int i = 0; i < n_bond; i++) { if (bond[i].curve == curve) { bond[i].exists = false; } } } void delete_curve_with_children(std::vector &atom, std::vector &bond, int n_atom, int n_bond, const potrace_path_t * const p) { delete_curve(atom, bond, n_atom, n_bond, p); potrace_path_t *child = p->childlist; while (child != NULL) { delete_curve(atom, bond, n_atom, n_bond, child); child = child->sibling; } } double angle_between_bonds(const std::vector &bond, int i, int j, const std::vector &atom) { return (angle4(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y)); } double bond_length(const std::vector &bond, int i, const std::vector &atom) { return (distance(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y)); } double distance_from_bond_y(double x0, double y0, double x1, double y1, double x, double y) { double d1 = distance(x0, y0, x1, y1); double cos = (x1 - x0) / d1; double sin = -(y1 - y0) / d1; double h = -(x - x0) * sin - (y - y0) * cos; return (h); } double distance_between_bonds(const std::vector &bond, int i, int j, const std::vector &atom) { /* double y1 = distance_from_bond_y(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].a].x, atom[bond[i].a].y); double y2 = distance_from_bond_y(atom[bond[j].a].x, atom[bond[j].a].y, atom[bond[j].b].x, atom[bond[j].b].y, atom[bond[i].b].x, atom[bond[i].b].y); if (fabs(y1 - y2) >= 4) return (FLT_MAX); double r1 = max(fabs(y1), fabs(y2)); */ double y3 = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].a].x, atom[bond[j].a].y); double y4 = distance_from_bond_y(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y, atom[bond[j].b].x, atom[bond[j].b].y); if (fabs(y3 - y4) >= 4) return (FLT_MAX); double r2 = std::max(fabs(y3), fabs(y4)); return (r2); } double distance_from_bond_x_a(double x0, double y0, double x1, double y1, double x, double y) { double d1 = distance(x0, y0, x1, y1); double cos = (x1 - x0) / d1; double sin = -(y1 - y0) / d1; double l = (x - x0) * cos - (y - y0) * sin; return (l); } double distance_from_bond_x_b(double x0, double y0, double x1, double y1, double x, double y) { double d1 = distance(x0, y0, x1, y1); double cos = (x1 - x0) / d1; double sin = -(y1 - y0) / d1; double l = (x - x0) * cos - (y - y0) * sin; return (l - d1); } double percentile75(const std::vector &bond, int n_bond, const std::vector &atom) { std::vector a; int n = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists) { a.push_back(bond_length(bond, i, atom)); n++; } if (n > 1) { std::sort(a.begin(), a.end()); int pos = 3 * (n - 1) / 4; return (a[pos]); } else return (10.0); } bool terminal_bond(int a, int b, const std::vector &bond, int n_bond) { bool terminal = true; for (int l = 0; l < n_bond; l++) if (l != b && bond[l].exists && (bond[l].a == a || bond[l].b == a)) terminal = false; return (terminal); } void debug_image(Image image, const std::vector &atom, int n_atom, const std::vector &bond, int n_bond, const std::string &fname) { image.modifyImage(); image.type(TrueColorType); image.strokeWidth(1); int max_x = image.columns(); int max_y = image.rows(); for (int i = 0; i < n_bond; i++) { if ((bond[i].exists) && (atom[bond[i].a].exists) && (atom[bond[i].b].exists)) { if (bond[i].type == 1) { image.strokeColor("green"); } else if (bond[i].type == 2) { image.strokeColor("yellow"); } else if (bond[i].type >= 3) { image.strokeColor("red"); } if (bond[i].hash) { image.strokeColor("blue"); } else if (bond[i].wedge) { image.strokeColor("purple"); } image.draw(DrawableLine(atom[bond[i].a].x, atom[bond[i].a].y, atom[bond[i].b].x, atom[bond[i].b].y)); } } for (int i = 0; i < n_atom; i++) { if (atom[i].exists) { if ((int(atom[i].x) < max_x) && (int(atom[i].y < max_y))) image.pixelColor(int(atom[i].x), int(atom[i].y), "blue"); } } image.write(fname); } void draw_square(Image &image, int x1, int y1, int x2, int y2, const std::string &color) { image.strokeWidth(1); image.strokeColor(color); image.draw(DrawableLine(x1, y1, x2, y1)); image.draw(DrawableLine(x1, y2, x2, y2)); image.draw(DrawableLine(x1, y1, x1, y2)); image.draw(DrawableLine(x2, y1, x2, y2)); } void draw_box(Image &image, std::vector &boxes, int n_boxes, const std::string &fname) { image.modifyImage(); image.type(TrueColorType); for (int i = 0; i < n_boxes; i++) { draw_square(image, boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2, "green"); } image.write(fname); } int count_pages(const std::string &input) { std::list imageList; readImages(&imageList, input); return (imageList.size()); } int count_pages(const Blob &blob) { std::list imageList; readImages(&imageList, blob); return (imageList.size()); } int count_atoms(const std::vector &atom, int n_atom) { int r = 0; for (int i = 0; i < n_atom; i++) if (atom[i].exists) r++; return (r); } int count_bonds(const std::vector &bond, int n_bond, int &bond_max_type) { int r = 0; for (int i = 0; i < n_bond; i++) if (bond[i].exists) { r++; if (bond[i].type>bond_max_type) bond_max_type = bond[i].type; } return (r); } bool detect_curve(std::vector &bond, int n_bond, const potrace_path_t * const curve) { bool res = false; for (int i = 0; i < n_bond; i++) if (bond[i].exists && bond[i].curve == curve && bond[i].type == 1 && !bond[i].wedge && !bond[i].hash) res = true; return (res); } // Igor Filippov - 2009. // The following two functions are adapted from ConfigFile /// // Class for reading named values from configuration files // Richard J. Wagner v2.1 24 May 2004 wagnerr@umich.edu // Copyright (c) 2004 Richard J. Wagner // // 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. void trim(std::string &s) { // Remove leading and trailing whitespace static const char whitespace[] = " \n\t\v\r\f"; s.erase(0, s.find_first_not_of(whitespace)); s.erase(s.find_last_not_of(whitespace) + 1U); } bool load_config_map(const std::string &file, std::map &out) { typedef std::string::size_type pos; const std::string& delim = " "; // separator const pos skip = delim.length(); // length of separator std::ifstream is(file.c_str()); if (!is) return false; while (is) { // Read an entire line at a time std::string line; std::getline(is, line); // Ignore comments //line = line.substr(0, line.find(comm)); if (line.length() == 0 || line.at(0) == '#') continue; // replace tabs with spaces pos t; while ((t = line.find('\t')) != std::string::npos) line[t] = ' '; // Parse the line if it contains a delimiter pos delimPos = line.find(delim); if (delimPos < std::string::npos) { // Extract the key std::string key = line.substr(0, delimPos); line.replace(0, delimPos + skip, ""); // Store key and value trim(key); trim(line); out[key] = line; // overwrites if key is repeated } } is.close(); return true; } bool comp_boxes(const box_t &aa, const box_t &bb) { if (aa.y2 < bb.y1) return (true); if (aa.y1 > bb.y2) return (false); if (aa.x1 > bb.x1) return (false); if (aa.x1 < bb.x1) return (true); return (false); } osra-2.1.3/src/mcdlutil.cpp0000664000175000017500000111006314115175251014261 0ustar igorigor/* -*-C++-*- ********************************************************************** Copyright (C) 2007,2008 by Sergei V. Trepalin sergey_trepalin@chemical-block.com Copyright (C) 2007,2008 by Andrei Gakh andrei.gakh@nnsa.doe.gov This file is part of the Open Babel project. For more information, see 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 2 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 GNU General Public License for more details. *********************************************************************** */ #include #include #include #include #include "mcdlutil.h" #include #include //#ifndef WIN32 #include //#endif using namespace std; namespace OpenBabel { #define CONNMAX 15 #define NATOMSMAX 255 #define NBONDSMAX 255 typedef struct adjustedlist { int nb; int adjusted[CONNMAX]; } adjustedlist; typedef adjustedlist neigbourlist[NATOMSMAX]; #define PI 3.141592653589793238462 #define blDenominator 4.0 //Controls bond legth in bondEnlarge #define nRotBondsMax 20 //Determines no. rotating bonds in correctOverlapped //Hydrogen valencies. Zero dummy element is the first const int hVal[NELEMMCDL] = { 0,1,0,0,0,3,4,3,2,1, 0,0,0,3,4,3,2,1,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,4,3,2,1,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 2,3,2,1,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,2,0,2,1,0,1,2,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0 }; const int maxVal[NELEMMCDL] = { 0,1,0,1,2,4,4,5,3,1, 0,1,2,4,4,6,6,7,0,1, 2,3,4,5,6,7,6,4,4,2, 2,3,4,5,6,7,8,1,2,3, 4,5,6,7,8,6,6,2,2,3, 4,5,6,7,8,1,2,3,4,4, 3,3,3,3,3,4,3,3,3,3, 3,3,4,5,6,7,8,6,6,3, 2,3,4,5,6,7,8,1,2,3, 4,5,6,6,6,6,3,4,3,3, 3,3,1,1,1,0,0,0,0,0, 0,8,1,8,5,0,0,0,0,0,0 }; const int chargeVal[NELEMMCDL] = //0 - dummy { 0,-1,-1,-1,-1,-1,-1, 1, 1, 1,-1, //Ne -1,-1,-1,-1, 1, 1, 1,-1,-1,-1, //Ca -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //Zn -1,-1, 1, 1, 1,-1,-1,-1,-1,-1, //Zr -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //Sn 1, 1, 1,-1,-1,-1,-1,-1,-1,-1, //Nd -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //Yb -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //Hg -1,-1, 1, 1, 1,-1,-1,-1,-1,-1, //Th -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, //Fm -1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1 }; const string aSymb[NELEMMCDL] = {"0", "H" ,"He","Li","Be","B" ,"C" ,"N" ,"O" ,"F" ,"Ne", "Na","Mg","Al","Si","P" ,"S" ,"Cl","Ar","K" ,"Ca", "Sc","Ti","V" ,"Cr","Mn","Fe","Co","Ni","Cu","Zn", "Ga","Ge","As","Se","Br","Kr","Rb","Sr","Y" ,"Zr", "Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn", "Sb","Te","I" ,"Xe","Cs","Ba","La","Ce","Pr","Nd", "Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb", "Lu","Hf","Ta","W" ,"Re","Os","Ir","Pt","Au","Hg", "Tl","Pb","Bi","Po","At","Rn","Fr","Ra","Ac","Th", "Pa","U" ,"Np","Pu","Am","Cm","Bk","Cf","Es","Fm", "Md","No","Lr","D" ,"" ,"G" ,"0" ,"Xx","" ,"" , "M" ,"X" ,"A" ,"Q" ,"" ,"" ,"" ,"" ,"" ,"" }; #define NEXACTATOMS 21 const int exactAtom[NEXACTATOMS]= {6,14,5,50,82,8,16,34,52,7,15,33,51,9,17,35,53,32,13,26,80}; #define NALKALYATOMS 5 const int alkaly[NALKALYATOMS] = {3,11,19,37,55}; #define NALKALYEARTHATOMS 5 const int alkalyEarth[NALKALYEARTHATOMS] = {4,12,20,38,56}; #define NTRIVALENTATOMS 31 const int trivalent[NTRIVALENTATOMS] = {21,31,39,49,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,81,89,90,91,92,93,94,95,96,97,98,99}; #define NTITANATOMS 3 const int titan[NTITANATOMS] = {22,40,72}; #define NVANADIUMATOMS 3 const int vanadium[NVANADIUMATOMS] = {23,41,73}; #define NCHROMIUMATOMS 3 const int cromium[NCHROMIUMATOMS] = {24,42,74}; #define NMANGANESEATOMS 3 const int manganeze[NMANGANESEATOMS] = {25,43,75}; #define NLIKEFEATOMS 2 const int likeFe[NLIKEFEATOMS] = {27,28}; #define NPLATINUMATOMS 6 const int platinum[NPLATINUMATOMS] = {44,45,46,76,77,78}; #define NCOPPERATOMS 3 const int copper[NCOPPERATOMS] = {29,47,79}; #define NZINKATOMS 2 const int zink[NZINKATOMS] = {30,48}; #define NMETALS 78 #define NHALOGENS 5 #define NHETERO 10 #define NAROMMAX 9 #define NLIGHT_METALS 20 #define NHEAVY_METALS 58 #define DEUTERIUM_ATOM 104 #define METALL_ATOM 111 #define HALOGEN_ATOM 112 #define ANY_ATOM 113 #define HETERO_ATOM 114 #define ANY_BOND 8 const int possibleAromatic [NAROMMAX] = {7,8,15,16,33,34,51,52,HETERO_ATOM}; const int metals[NMETALS] = { 3,4,11,12,13,19,20,21,22,23,24,25,26,27,28,29, 30,31,37,38,39,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,87,88,89,90,91, 92,93,94,95,96,97,98,99,100,101,102,103 }; //#define A B ??? - const int lightMetals[NLIGHT_METALS] = { 3,4,11,12,13,19,20,21,22,23,24,25,26,27,28,29,30,31,37,38 }; const int heavyMetals[NHEAVY_METALS] = { 39,40,41,42,43,44,45,46,47,48,49,50,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,87,88,89,90,91, 92,93,94,95,96,97,98,99,100,101,102,103 }; const int halogens[NHALOGENS] = {9,17,35,53,85}; const int hetero[NHETERO] = {7,8,14,15,16,33,34,51,52,84}; #define RUNDEF -1.2345678E9 #define DEFAULTBONDLENGTH 1.44 #define NDATABASE_MOLECULES 147 const string strData[NDATABASE_MOLECULES]= { "05A53816506A53810000A00008692A99998857A6567442806020101030401050203050504", "06A25008659A74998659A99994330A74990000A25000000A00004330100203030405060106060406020305010305020104", "06A48805837A48800000A00007834A90467987A59523994A999962970802010103040106040502060503050302", "06A33680000A00003697A90943697A90949999A00009999E3368717109020101030203010602050304050606040405", "06A00002886A49980000A99992886A71137887A28867887E49985773070102020303060601010503040405", "07A00002500A00007499A86397499A86392500A43200000A69404999A4235999909070203070304050402030102050606070105", "07A18315489A81685489A18311830A81681830A49990000A00008658A9999865809050403050204010301060602020707010607", "07A51830000A99995257A61733821A00008921A86408921A16055257E45686533100604030601070704050702050301020304050602", "07A43300000A86602500A00002500A86607499A00007499A43309999A213550000801020301050606040402050301070607", "07A69274000A69270000A00000000A00004000A69277999A34649999E3464599909010203040407010707060605050104060203", "07A43309999A00007499A86607499A00002499A86602499A43300000E433049990806050406020101030305020406070702", "08A99994342A99991475A50354342A74810040A50351475A00004363A24800000A00001455100405050301020402030107080806070506030704", "08A14437499A43307499A57735000A43302500A14432500A00005000A57730000A57739999100102020303040405050601060407020808060607", "08A55684926A59770000A13416715A91136862A00003196A24094926A99995337E654534311106030806020101030401070408020708060503050207", "08A00003799A00009999A21890000A83936200A83930000A61999999E21896200E6199379912070206040206010203010805010806080703030507040405", "08A36607320A78867320A99993660A78860000A36600000A15473660A00002113A0000520709010202030304040505060106050701080807", "08A00005000A00008332A57705000A28419999A57708332A57701667A29290000A0000166709010202040405050301030306060707080801", "08A48819390A53570000A00007833A90467986A59523994A11895837A99996297E48815837100603050602080803040807040502070503010401", "08A19105257A80890000A99992628A19100000A00002628A80895257E50001004E500042531101080807070404050105060303020806070204020601", "08A50000000A50006666A00002886A16670000A83320000A99992886E66662886E33332886100108080303040401010707060605050106020302", "08A25008659A74998659A99994330A74990000A25000000A00004330E30622562E6937256209010202030304040505060106060703080708", "09A24994330A74994330A24991444A74991444A49990000A99995773A00005773A85558273A144382731105040305020401030602010607010207090708060908", "09A55375090A55370002A12826830A91696964A64713483A18610000A99995492A00003324E2320509012090305090201010304010704050207050908080308060602", "09A18510000A00003211A90859999A70960000A00008451A52418451A70965241E52413211E18515241130905050602050102080402080608090101040907070403070603", "09A19105257A49994253A49991004A19100000A00002628A80895257A99992628A80890000E386426281101020203030404050105060707080206030805090907", "09A49994252A80895257A49991004A80890000A19090000A00002628A19095257A99992628E218626281101020208080404030103030505060607070103090109", "09A00003622A10641208A32581208A49670000A32583622A99993247A81610000A68065661E593523311101020203030404050501030909070706060808050407", "09A31703170A50000000A00004999A36604999A68293170A63394999A99994999A81698169A18308169100102010303090904040502050106070507080806", "09A82718548A41359999A91956942A00008341A44961502A55014870A11246476A55010000E449664761107040607090401090301030609050608050804020102", "09A70330000A29660000A00002881A00007010A29669891A70339891A99997010A99992881E63994946100102020303040405050606070708010802090509", "09A80419999A43868037A89488452A00009735A15210000A53806452A11118037A53801698E43863169100704060702040102030103060209060805090805", "09A10176779A34868279A63437716A99994155A63430500A34860000A10171406A00004155E26634155100102020303090905050606070708010803040504", "09A16675773A50005773A83325773A50000000A16670000A00002886A83320000A99992886E66662886100102020909040405050601060407070808030302", "09A70296185A40884515A09414515A42940000A00006071A88530000A99994478E46463112E779448571103050803040202050102080409080604070109070706", "10A43309999A86597499A72165000A43305000A28867499A86592500A43300000A28862500A00002500A0000749912020303040405010507080306040810050809091001020607", "10A46515888A50291200A06397575A81687425A55524463A17730000A16865888A88076225A00002513A9999993812070305070201010304010502080509070602090610080410", "10A48804396A48801478A00006392A90466546A59522553A11914396A99994857A59520000A69959494A2962949412060305060201010304010704070505080208040903101009", "10A25474570A16166133A62753127A54374570A54370000A94454931A86996254A38217369A00008932A1791999912010203010504040207040607030506030408020909100810", "10A00006995A25904430A25900855A66250000A99991632A46986995A63859327A85547850A85544430E46981632130201030204030410010607060807090802090509040510060510", "10A53026409A85668294A00008578A65814346A12696409A99996433A51190000A12692065A65810691E53022065130503040510010103040604091008080508070709091001020206", "10A99994745A58033967A85242714A58030000A24865324A65322714A00002818A82426113A18370000E32963967131005061004020205030106040306020808010409090707051007", "10A38760000A23092718A00008169A83160000A23097153A99998464A83164436A67457153E67452718E387644361405080205090402090809100101041007070406070806020103100503", "10A36631099A20423911A00000000A82571099A20428500A99999856A82575689A66328500E66323911E366356891410050508020509040209080910010104100707040607080603020103", "10A73929797A99996506A32668429A26361664A99991664A00009797A63030000A26366506E32663734E68198060130806100802010103030610020804030902050407050709050709", "10A00007120A10935313A99996759A14849577A54623723A48450000A82347300A63309577A89634915E4845531312020105020610100107100506090504010708040803070903", "10A18301830A50000000A81691830A99995000A00005000A18308169A50009999A81698169E50006340E5000366112010202030310100101050506060909080807070608040403", "10A62500000A25002165A76109666A99992165A25006783A40838511A99996491A00009666E74798226E4083374912020110010401070406100502080506080905070903060703", "10A73929796A99996506A32668429A26361663A99991663A00009796A63030000A26366506E32663734E6819805912080610080201010303061002080403090205040705070905", "11A11600000A46320000A57051522A28912486A00761522A28147513A00008476A10849999A45569999A56288476A284451212002010302070608070910100609080405050104030511041101110211110311060811071111091110", "12A86832482A65403688A00002482A21983688A43972482A43970000A21986311A00007446A43977446A65406311A43979999A8683744630030808111112120101060306060504020209090407050510100701021012091107080403010501101202120911101107080908040307030506020604", "11A55476610A55471550A30757567A91868524A65015060A24036610A99997020A32150000A24039664A00004877A00001550130506020101030401070405020705080210061110081109060309", "11A99994719A35190000A50464719A64810000A90277646A00004838A09727765A41667765A60187646A74542927A25462927131003041002111103040206071106030808070905030910010105", "11A54717869A10769779A91039894A00005034A21977869A99998274A65021620A65026365A05821505A19730000E547130661405020805010203010603060801110807110709111007100910040504", "11A63167961A37420000A00005471A63165546A43862037A09653395A68423395A00008150A18139999A47959999E342139991406030506021111030411070405020705030808090910100106070401", "11A50227868A00000000A06467868A63146852A06463611A63142407A88964074A18652407A30859999E18656852E5022361115011111050503060808100410010411060310090309010607070408020205", "11A33586997A99992256A00007945A62567945A51370000A20545575A82195337A20541779A61486761A86960000E33583081130805110501110608030601030906070904010704070205101002", "11A46588688A46585901A23299999A23294644A00008688A00005901A85963661A85960000A23290000E41501367E597136611405060406020401020305030107080207040904101011071108110908", "11A74236368A00008420A80755210A12646263A46091158A53023684A20804895A53020000A61998579A22849999E46094895130704060711040111030103061105060805080901100902100402", "11A31388599A13560000A00009949A58129999A37597499A07368599A63938949A45724300A05043100A17822600E31384849130603050611010103040107040511070510020810110909020508", "11A00004220A00001358A24810000A50001358A74818488A24815626A24818488A74815626A99994220A99991358E50004220130201030204031104061101060706081109081009041005080705", "11A85916567A35746567A99992562A23462655A00009594A49450000A23469594A12637033A79770000E48375123E758051231402040102031111101004030111010406090609030810070205080705", "11A09557693A34559510A65449510A90447693A99994755A90441816A65440000A34550000A09551816A00004755E3675475512010202030304040505060607070808090910011007110311", "11A00009999A42230000A00006825A78729999A52073492A10255079A87345449A87348517E10258517E78726930E422350791406030506021111031011071005020705090608090708041001040301", "11A13393318A13746318A70459999A49670000A86644773A44732909A00005045A90889045A21849999E78206545E49674999130601110210110506110406040702010708100508090210030903", "11A99995000A81698169A81691829A00005000A50009998A50000000A18301829A18308169E50006338E31703169E6830316912070408041004050802050102030106030706110109111009", "11A49991430A25005766A99991430A49997229A00001430A25000000A74990000A74995766E74992892E49994337E2500289212050606010701030904080201110510091110100803070402", "12A49997216A25008659A49994330A00007216A25002886A00004330A74992886A99994330A74998659A99997216A25000000A749900001405030605040202010103040609011009080707030810051107121211", "12A74852867A74850000A44730000A95313961A80111961A56432867A99993093A21052452A00004640A53805093A24853885E5058388515061205060201011204010704050207050809120801101011110903080203", "12A00003057A47044586A99994586A47041529A99991529A73520000A47047643A73529172A99997643A17970584A17975530E735261151406050406021203120305020402070708080909030211100101110410", "12A40419999A55880712A00007357A17531277A55884325A17535184A90205891A90209166A34830000E81107504E41302134E41306289160603050611121203101207100708030108010507050211040406040909020211", "12A83909999A37070000A00005869A00008652A44813000A08764391A75144695A68408652A75147390E08767390E68405999E37074391160603050602121203111207110502070510060910070908110408030401080901", "12A67547130A27484678A78944678A27481245A78941245A31286036A53210000A99995885A00006262A08487356E31282716E570157721412021203020406110305040711070507090210061009080301080601", "13A00008749A21667501A21669999A46659999A68308749A46657501A21662499A46652499A68301250A46650000A21660000A00001250A3501512124010201030304040505060602021313070708080909101011111212071311131013081309131213011303130413061305", "12A98975714A74231429A49490000A24741429A98978571A00005714A00008571A24749999A74239999E49498571E24744285E7423428512120202030304041111060607070808101009090505010112", "13A28577422A57147422A71424948A57142474A28572474A14284948A99994948A71420000A99990000A71429897A99999897A00002474A00007422160102020303040405050601060307040808090907021010111107051201131312", "13A99994826A91595517A83512806A74203552A54942806A94380000A44703607A85380666A16353603A25602863A13885888A00001596A08320912160507030504070204010201030408030608060907111010051310121309111209", "13A39485622A00003358A32469999A69013962A30124453A23988376A61112528A85093962A92982528A68711132A68710000A92980000E84791132160603050602010103040107040502070510130710040808090913101111120912", "13A60776420A66072578A90957683A28148103A52614999A83206052A20806788A00005578A07342947A07340000A20809999A52610000E40781315160506020101030401020507050807090813090213091012100512071104110603", "13A63116880A88436880A88432580A99994701A50224701A63110000A37776880A12442580A12446880A00004701A37770000A37772580A6311258015030413030501010202040513091007090512120808100507111206131106", "13A66660962A66662887A83333849A99992887A99990962A83330000A50000000A33330962A33332887A16660000A00000962A00002887A1666384915010202030304040505060106070808090107101111121213081009130209", "13A22073757A07045817A48844666A00000000A07048909A68573757A25832121A52145817A29129999A52148909A22071212E48847575E25834909160806130802010103030613020810031202051009120905091101071304110704", "13A18232941A00005293A47863921A77493823A00008725A71412941A22020980A51665293A25839999A51668725A18230000E47867254E22024215161308020101030306130208100312020510091209050911010713110704080604", "13A85754958A99992462A99997437A00004924A14072462A85750000A85759898A42717437A42712462A14077437A57289898A57280000E5728495815020103011301060207030813091311081209040510080509120610040711", "13A00008994A36698934A00006804A18349999A55028934A36693254A54560000A18345739A73371064A73373195A36691064E55024260E36696804160103080313080213040106110402091007090612121011071205020513100806", "13A60780000A67532108A39210000A32242108A10892108A89102108A00003963A99993963A39215201A60785201A79516296A20486296E501033451404031304020101031302050406020705080609101110120907120811", "13A60520000A10660521A39180000A20476299A10822113A89172113A00003961A99993961A79526299E60525168E39185168E32162113E675421131412031301010305120613070508061110091004110704080905021302", "14A22012921A22010284A00003814A43083895A26732069A05352921A44973124A67292962A64462313A99992313A84583976A71380000A92750000E74831014190603050602010103040107040502070514120814110909120709081113121413101113100408", "14A18759999A29620000A01974012A48374140A33072038A08392993A53803184A09386433A20735732A53808661A48875732A00008025E40976433E271529932006030506021414030414070405020705080306091304071108130911120809121011131001121001", "14A37508659A62498659A74996495A62494330A37504330A25006495A74992165A62490000A37500000A25002165A00002165A00006495A99996495A999921651701020203030404050506010607080809091004070510120610110313071411121314", "14A52504032A47494920A31242823A25623791A06253791A31240000A00004839A25620887A68756129A74375161A93755161A68758952A99994113A743780641705070305040702040102010304080306080611130911101301100209101409121412", "14A49995955A36928217A99993692A49992263A00008217A86945955A13032263A00004524A63070000A99990000E86942263E63073692E36924524E130359552314050201050208050708130408130213140707041401010411100904100903100603120103120912110606011104", "14A14989134A00007637A67720000A47251181A79534409A99995591A79536772A67722362A55916772A55914409A11815591A35457953A35453228A35455591161314121411141014091405100605070609070810041303040803021101120201", "14A00002151A10824678A74260000A21050075A57591849A38601849A29535658A38604678A57594678A64325658A85664678A99992037E29532829E6432282918130414130305050606040314071308060905101411091011121103120208070201020401", "14A57093413A57090749A79674204A79030000A00002206A99991915A39023454A39020749A16774204A17100083E30962955E30961415E65801415E658027892002040102031414131304030108100708091111121210090708021312060403060510090511071401", "14A00001443A00004330A25005728A60475728A50001443A25000000A75000000A99991443A99994330A85566830E25002887E75005773E60472887E5000433018010202030314140505060106050707080809071301111113141212090910100404030413", "14A14499999A00347478A43809999A58294608A58297478A00344608A00002521A58292521A43800000A14490000A28641391E43804999E14494999E286432171713020201050312051213030107130812090810071110091106020405041406141114", "14A19889999A45149217A66708216A88286043A70752304A13482174A00004521A12467216A70750000A44130000E29313695E56946043E40096303E4009313018131403130403050414050614070608071308071110110910050912110412021201020801", "14A60520641A99990000A38890641A00880000A10822716A88592716A00004565A99114565A20476866A78946866E60525772E38895772E32162716E6695271615130314010103051306140705080612111011091207090810050406020204", "15A15004047A35682933A10222610A35680000A59083989A28862199A54313197A00003461A72723050A67272258A90903461A99991848A75220440A93850440E795414082006070402020501020604030603080108050907101109131012111413141207051509151410111315", "14A99993463A89991732A89995196A60000000A39990000A60006927A10001732A00003463A10005196A39996927E29995196E29991732E70005196E700017321401020214140404050512120707080809091111101006061313030301", "15A14543959A34772874A10002551A34770000A57953929A28402111A53183108A00003402A71132991A65682229A93173842A92260411A99991877A73630411E84542874190607040202050102060403060308010805090710110915101315141014091311121412130705", "15A46187999A69287999A80825999A69284000A46184000A34645999A34642000A11552000A00004000A11555999A80822000A69280000A46180000A00007999A3464999918010202030304040505060106070808090910050706101112121313070411150110141415", "15A33335773A50006735A33333849A66665773A50002886A66663849A16662886A00003849A16666735A00005773A50000962A66660000A83330962A83334811A9999192418050603050102020404060103091001090307070808100511111212130613061413151514", "14A24498659A00004286A50331429A26170000A75508572A99994286A74490000A50337230E75505715E24495715E74492814E26172814E42283637E6140510915081414131303110703070906110608050905030412041202100210010801", "16A73181418A16684435A37326615A33468273A10227107A00004783A68276769A83024030A99994030A95756586A76088273A63930000A33840000A24491418A50242305A5024478330030208070908101111070910040305060602040513121413150101121415141613161516121601161608160916101607161116040316160506161602", "15A25446903A08194678A53505998A29245772A08191396A75436903A00005998A99992565A32750226A57301396A85660000A75431207A99995508E57304678E53502829191406041401030306040214100315020510091509050907010207130608131208151211081011", "16A70462641A70460264A90353433A51463508A66071886A88590264A49702829A29532716A32162075A00002075A13743584A26020000A99991773A06730000E22810906E853726412316030516020101030401070405020705151208151109091207090811131603131412151410111410040806130206", "16A38483333A57733333A67351666A57730000A38480000A28861666A28865000A38486666A57736666A28868333A38489999A57739999A67358333A09625000A00006666A09628333190102020303040405050601060708080901071011111212130810091307141415151616100209", "16A59953466A69901721A69905199A40043466A99993466A09841721A89921721A89925199A59950000A59956920A29865199A29861721A09845199A40046920A40040000A00003442190201030104010508070208030902100311041204141115121606131106120705150913161014", "15A71629322A12851233A50830000A00003000A71620675A50839999A95106091A00006997A95103906A12858764E76162814E34588536E76167185E08895000E3458146215150202040414110505030315130707090911120606010113140808101012", "16A40003464A59993464A69991732A59990000A40000000A30001732A30005196A40006928A59996928A69995196A89995196A99993464A89991732A10001732A00003464A1000519618020303040405050601060708080909100107021010111112121303131415151616070614", "16A61204512A61201863A99992864A46586454A38260538A15304689A25005747A15301863A99990000A00006454A71320000A89854620E71322864E89851869E25002832E457855732308051505020501020715060810060710160601160407010413011203011211021409021412141311110913030309", "16A21912563A23970000A00003476A39953505A26941767A05712563A44522740A81270648A55482740A99992091A63922003A61874654A99993329E89262740E66443329E5091350521060305060201010304010704050207050416070915110911161215120914151313141310141011081008", "16A34524404A83733226A38885581A99991895A22621178A42462150A14683840A83734659A68645581A63880000A83730000A00001843A06740461E67061178E38883226E63882407190605140602161615150502140315080209080309101611100402041113051213071201071501", "16A21141056A39430000A60560000A78861056A78864045A60565101A00001056A99991056A99994045A00004045A21144045A39435101E39432113E60562113E39432988E6056298818010202030304041414131301010704080809071010111112120606051115151616050509", "16A89991732A99993464A69991732A59990000A40000000A30001732A10001732A00003464A10005196A89995196A40006928A59996928E69995196E30005196E59993464E400034641716151503030404050506160606070708080909141411111212131310100202010103", "17A57140000A42850825A71430825A42852474A71432474A85720000A28563299A57143299A99992474A99990825A28564949A14282474A57144949A42855774A14285774A00003299A000049492009100503030606100805040202010103070411071308141304081114121616170712151117150509", "16A49994173A99990759A00001897A87120000A44480000A15444316A14701043A99993083A84913984A00003510E30513367E69853225E69110901E29411897E72051897E485210911705141305041515161614130407140204070310061003080908020611091211010112", "16A00003849A16670962A33330000A16676735A66660000A83320962A33337697A99993849A66667697A83326735E83324810E49996735E83322887E49990962E16674810E16672887161602020303141405050606131308081111101009091212070704041515010116", "17A06651153A56331153A00007272A15759999A00009090A56332970A15754545A06652970A47249999A62989090A62987272A47244545A31490000E31493636E47246363E31499090E1575636318041605040317030507171407121512141115100909161011070812060602080113011302", "18A69995196A89995196A99993464A89991732A69991732A59993464A59996927A69998659A89998659A99996927A40003464A30005196A40006927A59990000A40000000A30001732A10001732A00003464220102020303040405050601060708080909100107021006111112121307130514141515161116171816171812", "18A40005196A59995196A69993464A59991732A40001732A30003464A69996928A89996928A99995196A89993464A10003464A00005196A10006928A30006928A49993464A69990000A49990000A49998609220102020303040405050601060708080909100207031011121213131406110114021504161617171514180718", "17A99992891A49990000A99990952A00000952A83330000A16660000A83335782A00002891A66668673A33338673A16665782E33336733E16663843E66666733E66660952E33330952E833338431716021502051506160717140709141012121111131308080404060305010317011009", "18A50003750A50001587A99990000A38055337A31250504A12503896A20424760A12501587A87493750A00005337A79570577A61940000A68744833A87491441E62590719E20422378E79572959E374046172308051605020501020716060810060710180601180407010409131713011311171409031411031514021512110212", "18A26160872A41280000A58720000A73830872A91270872A99992383A08720872A00002383A08723893A26163893A91273893A41284765A73833893A58724765E58723021E41283021E41281744E587217442001020203030404181817170101070708080909101016161515131314141212101311110606050504", "19A57143298A71432474A57144949A42852474A57141649A71430824A99992474A42855773A42850824A28563298A42854124A85720000A57140000A99990824A28564949A14282474A14285773A00003298A00004949220714020606121214010204090913130610041510030108030401150816181819101617151917110405010207", "19A29138599A32545200A00009919A53629999A72535319A07138599A60138879A75947359A50524360A46493040A52393200A47120000A33172040A64782279A35650680A61060920E39992279E59512080E3595759924060319060201010304010704190207191009181016141411110918161013111716121315171512150708020909050508", "18A83338796A16660979A00003911A33330000A00005865A99995865A66669775A66660000A16668796A83330979A33339775A99993911E50008796E83332932E16666842E50000979E16662932E8333684218081610081410121406121806041602041702031705031505091511091311071301071801", "20A93358165A33223593A66773593A33221633A16774532A66771633A83544532A16770694A50000694A00003593A50004532A99993593A00001633A99991633A75940000A90500000A75947145A66775470A93356205E8322069424101305100204040809040609030611020813070312141420200602051103120715061614151618031907171801191701", "21A51751924A51750189A66372528A37722565A48541396A62861924A36552075A21641999A23691509A95021396A09942641A19000000A04970000A00001509A70751509A74262150A88882603A84492037A99992037A84493924E169606792906030506020101030401070405020705211208211109091207090811131221131411131404081506161516031910171920181810151817201617", "20A83200000A66400000A99990000A33985090A66795090A83205090A17585090A99993327A99991714A50000000A33200000A16400000A00001714A00003427A00005090A00000000A99995090A50005090E50001714E5000332721010302011002051806051706081709080309191011101211161213161415071504071804201813141920", "21A99995173A38633013A26931504A22882200A92786421A22880000A04432955A60151579A55582231A55580149A76552418A72793029A00001158A09390000A80304259A84673649A80305700A99993649E94724259E08932200E4223237430200703200604040702042102030621030821100909021209111208101108201313071314140612151116151717161519161818191705050101191801", "21A93358655A33224083A66774083A33222123A16785022A66772123A83545022A16781184A49050000A00004083A50005022A99994083A00002123A99992123A75940490A90500490A75947635A66775961A93356696E83221184E500011842610130510020404082104062103061102081307031214142020060205110312071506161415161803190717180119170102092009", "22A33333849A33335773A50006735A66665773A66663849A50002886A83336735A99995773A99993849A83332886A16672886A00003849A00005773A16676735A16678659A33339622A50008659A83338659A83330962A66660000A50000962A16670962280102020303040405050601060708080909100407051011121213131401110214151616171703141507181920202121061019112221221718", "22A30772664A46152664A53841332A46150000A30770000A23081332A53843997A69223997A76912664A69221332A76915329A92305329A99993997A92302664A46155329A53846661A69226661A23083997A30775329A07691332A00002664A0769399727010202030304040505060106070808090910020703101112121313140811091407151516161711170118181915190620202121221822", "22A30008660A10008660A00006928A10005196A00003464A10001732A69998660A89998660A99996928A89995196A99993464A89991732A69991732A60000000A40000000A30001732E40006928E30005196E40003464E60003464E69995196E6000692827222121202019191818172217070808090910220721101011111212132013141515161619131406050504041816060302020101170403", "24A16673849A16675773A33336735A50005773A50003849A33332886A66666735A83325773A83323849A66662886A66660962A50000000A33330962A33338659A50009621A66668659A99998659A99996735A00006735A00008659A00000962A00002886A99992886A99990962310102020303040405050601060708080909100407051011121213130610110314141515160716171818080219192021220122232409232113112417161420", "24A31693170A31691056A49990000A68301056A68303170A89433170A99995000A89436830A68306830A68308943A49999999A31698943A31696830A10566830A00005000A10563170A86600000A99991340A99998660A86609999A13399999A00008660A00001340A13390000280102020303040405050606070708080909101011111212131314141515161601041706181817081910202019122114222221162302242423", "25A68361042A10676736A10673126A32261042A89323158A68368895A31618895A89326811A14140000A99751377A86340000A00001302A50240000A00004938A99754987A49499999A86349925A13409925A00008548A99758598A68366811A32263126A31616736A68363158A494949873224252325222521250124022322030422052406210723082109041005110112031304140315081606170618071902200820171505071601130214120918191110", "24A76718683A87797576A96755665A96754023A89382063A77440869A58510000A41080000A59929999A38329999A20659069A06467650A00005731A00003835A08841987A21650706E82286173E63078094E35928094E16716173E16713458E35921537E63071537E8228345824100909181019121111191220141313201421161515211622070808220723050606230524030404240317010202170118", "26A99991663A82650000A66210000A99990000A33330000A16890000A99993039A00001605A00003268A00006537A00008200A16899804A33339804A99996422A49770000A66669804A83099804A99998142A99999804A49779804A99994759A00009804A00004931A00000000E49776651E4977321127040102040302150326150515060524060824090823091023111022111222131220132520162017161917181914182114072101072526", "28A49997302A49992697A73025000A26975000A20233831A38327976A61687976A31576808A79763831A79766167A68086841A38322023A61682023A68423191A31573191A06743831A00005000A06746167A38320674A49990000A61680674A93253831A99995000A93256167A61689324A49999998A38329324A202361673204050106010706080309031010111107021202131314140912151505051616171718121919202021211309222223232424100725252626272706042828182808", "28A26160872A41280000A58720000A73830872A91270872A99995871A08720872A08722616A99994127A91272616A08727383A00005871A00004127A08729127A26169127A91277383A91279127A73839127A58729999A41289999E58728255E41288255E82555871E82554127E17445871E17444127E41281744E587217443201020203030404282827270101070708082626252511111212131308111414151522222121181819192020151817171616232324241010090906061610050504", "30A44366431A33185790A22067722A55474499A55475790A44363849A44367722A33184499A22142574A78864569A33331924A67136525A88962558A44440000A33330649A66663849A22143849A78635797A55630649A99991924A00006423A11115774A11114499A88800015A99990649A66660000A77770649A00087746A89043888A2214647033020103070405050130020604070108020917291010181109120513291415151116041723181219142013280321282221232224252520262727240806300316102619", "36A42856185A57146185A64284948A57143711A42853711A35714948A64282474A57141237A42851237A35712474A78564948A85713711A78562474A64287422A78567422A85716185A35717422A42858659A57148659A21434948A14296185A21437422A21432474A14293711A35710000A14291237A00003711A00006185A14298659A35719896A64289896A85718659A99996185A99993711A85711237A6428000048010202030304040505060106070808090910040705100311111212130713021414151516111601171718181914190620202121221722102323242024262309252821242730182229321519313412163336081335262536353433323130292827", "42A17323500A17326499A08665999A17327499A17322500A08664000A08665000A08662000A00002500A00003500A00006499A00007499A08667999A43302000A34641500A51961500A25982000A43307999A34648499A51968499A25987999A69286499A77935999A69287499A69283500A77934000A69282500A60622000A77935000A60627999A51960500A43300000A34640500A86593500A86592500A77932000A77937999A86597499A86596499A34649499A43309999A5196949948020302040105010603070706050808090910061011121213130403111415141615171705181918201921210422232224252625272728281626292923243030203132323333151631343535363627263424373738383923391940404141422042", "60A21159049A17778821A32689182A51019771A54399999A39459638A05577213A35427935A65078888A71849343A47678759A11047581A85878461A82498233A95786811A90306443A08305967A55447753A98515564A63245965A23246328A67698577A37587292A06883953A19256703A01416446A47485643A17834689A00004435A80685310A77586929A31054538A35284033A08213556A02733187A12641538A20943069A51044356A60942706A16031765A33451110A47510228A43082245A91646046A50851240A67475461A75283670A30831422A79273295A87472418A26680656A44130000A63102064A65830816A59060361A90224032A97103553A77360950A92952786A8075117890010201060112020302070304030804050409050605100611071707260818082109140918101310221122112312251226131413151416151915441619163017211724182019572030203821322231232523272435243725282629273327462833283429342935304931443146323732383343344035363640365137483839394539494041414241434252425443534456454845554647475347564851495050575058515252555354546055585659575958605960", }; #define NBONDTYPES 11 const int bondValence[NBONDTYPES] = {1,2,3,1,1,0,0,0,1,1,1}; const string fsastart="{SA:"; const string fsbstart="{SB:"; string intToStr(int k) { char temp[16]; sprintf(temp,"%d",k); string line=temp; return line; }; //------------------------------------------------------------------- class Rect { public: double left,top,right,bottom; }; class Point { public: double x,y; }; //------------------------------------------------------------------- class TSingleAtom { public: short int na; /*Atom position in the Periodic Table (nucley charge)*/ short int nv; /*Current atom's valence*/ short int nc; /*-9..+9 atom's charge*/ short int iz; /*Isotope difference between round(AtomMass)*/ double rx; /*Internal X-coordinate representation*/ double ry; /*Internal Y-coordinate representation*/ short int rl; /*radical label*/ short int nb; /*Number of neightboring atoms*/ short int currvalence; /*Valence, excluding non-defined hydrogen atoms. Equal sum of valences for each bond type.*/ short int special; /*Special=0 - no special definition =1 - no other atoms, except explicitly defined, can be connected with the atom (query structure)*/ short int ac[CONNMAX]; /*atom's numbers in array ATOM, which are connected with the atom selected*/ short int astereo; /*=0 - no stereo, =1 - R, = 2 -S, = 3 - unknown*/ string anum; int enumerator; int fragIndex; TSingleAtom() { na=6; nv=hVal[na]; nc=0; iz=0; nb=0; rl=0; currvalence=0; special=0; astereo=0; enumerator=0; fragIndex=0; }; TSingleAtom * clone(); int encoder(); int chargeConversion(); int valencyConversion(); int allAtAtom(); void atomCopy(TSingleAtom * source); static bool atomEquivalent(TSingleAtom * structure, TSingleAtom * query, int nHStr, int nHQuery, bool chargeSensitivity, bool isotopeSensitivity); static int chargeDeltaValency(int atomNo); }; class TSingleBond { public: short int tb; /*Bond type: 1 - single 2 - double 3 - triple 4 - aromathic (query) 5 - single/double (query) 6 - coordinational 7 - not used 8 - any (query) 9 - up 10 - down 11 - either*/ short int at[2]; /*Bond definition-atoms number in array ATOM*/ short int db; /*Ring/Chain conditions*/ short int bstereo; /* =0 - no, =1 - E , = 2 - Z, =3 E/Z*/ short int special; int enumerator; /*=1 - Chain (query) =2 - Ring (query) =3 - CIS/TRANS =4 - CIS/TRANS+Chain; =5 - CIS/TRANS+Ring;*/ TSingleBond * clone(); int bondConversion(); void bondCopy(TSingleBond * source); static bool bondEquivalent(TSingleBond * sBond, TSingleBond * qBond); int getValence(); }; //------------------------------------------------------------------- TSingleAtom * TSingleAtom::clone() { TSingleAtom * result; result=new TSingleAtom(); result->astereo=this->astereo; for (int i=0; iac[i]=this->ac[i]; result->currvalence=this->currvalence; result->iz=this->iz; result->na=this->na; result->nb=this->nb; result->nc=this->nc; result->nv=this->nv; result->rl=this->rl; result->rx=this->rx; result->ry=this->ry; result->special=this->special; result->enumerator=this->enumerator; result->fragIndex=this->fragIndex; result->anum=this->anum; return result; }; int TSingleAtom::chargeDeltaValency(int atomNo) { int result=-1; if (atomNo < NELEMMCDL) result=chargeVal[atomNo]; return result; }; int TSingleAtom::encoder() { // For given atom's number ATN in array ATOM returns a number from 1 to 32. // Atoms with related properties have the same number. It is required to reduce // the possible number of fragments in structure} int i; for (i=0; i0 // 0 - charge=0 if (nc < 0) return 2; if (nc > 0) return 1; return 0; }; int TSingleAtom::valencyConversion() { //for atom's number ATN in array ATOM returns some connected with valency value: // =2-Valence of ATN is less, then usual // =1-Valence of ATN is more, then usual // =0-usual valency int k1,k2; int result=0; //Default hydrogen k1=nv; k1=k1-currvalence-abs(nc)-rl; if (k1<0) k1=0; k2=hVal[na]; k2=k2-currvalence-abs(nc)-rl; if (k2<0) k2=0; if (k1 == k2) result=0; else if (k1 < k2) result=1; else result=2; return result; }; int TSingleAtom::allAtAtom() { //Define a digital representation of atom, they include: // a) Position of atom in the Periodic System // b) Its charge // c) Its valency} int b1,b2,b3,w; b1=encoder(); b2=chargeConversion(); b3=valencyConversion(); w=b3; w=w << 2; w=w+b2; w=w << 5; w=w+b1; if (rl != 0) w= ~w; return w; }; void TSingleAtom::atomCopy(TSingleAtom * source) { this->astereo=source->astereo; for (int i=0; iac[i]=source->ac[i]; this->currvalence=source->currvalence; this->iz=source->iz; this->na=source->na; this->nb=source->nb; this->nc=source->nc; this->nv=source->nv; this->rl=source->rl; this->rx=source->rx; this->ry=source->ry; this->special=source->special; }; bool TSingleAtom::atomEquivalent(TSingleAtom * structure, TSingleAtom * query, int nHStr, int nHQuery, bool chargeSensitivity, bool isotopeSensitivity) { bool result=false; int i,j,k; if ((structure == NULL) || (query == NULL)) return result; i=nHQuery; if (i>0) //if explicitly defined H is accociated with query atom { j=structure->nv; k=abs(structure->nc); if (k>9) k=k-9; j=j-structure->currvalence-k; if (j<0) j=0; j=j+nHStr; if (i>j) return result; //on corresponding structure atom must be at least the same number of H} } if (((structure->nc != query->nc) && chargeSensitivity) || ((structure->rl != query->rl) && chargeSensitivity) || ((structure->iz != query->iz) && isotopeSensitivity)) return result; //non-equivalency of Charge and Isotop is case of exact matching of these attributes i=query->special; //'NO OTHER' attribute on query if (i == 1) if ((structure->nb-nHStr) != query->nb) return result; //'NO OTHER' means, that number of neighbour in query must be the same as in structure //Checking for equivalent position in the Periodic Table if (structure->na == query->na) result=true; else { if (query->na == METALL_ATOM) //Metall checking { for (i=0; ina == metals[i]) { result=true; break; } } if (query->na == HALOGEN_ATOM) //Halogen checking { for (i=0; ina == halogens[i]) { result=true; break; } } if (query->na == HETERO_ATOM) //Hetero checking { for (i=0; ina == hetero[i]) { result=true; break; } } if (query->na == ANY_ATOM) result=true; //Any atom } return result; }; //---------------- TSingleBond methods ---------------------- TSingleBond * TSingleBond::clone() { TSingleBond * result; result=new TSingleBond(); result->at[0]=this->at[0]; result->at[1]=this->at[1]; result->bstereo=this->bstereo; result->db=this->db; result->special=this->special; result->tb=this->tb; result->enumerator=this->enumerator; return result; }; int TSingleBond::getValence() { int result=0; if (this->tb <=NBONDTYPES) result=bondValence[this->tb-1]; return result; }; bool TSingleBond::bondEquivalent(TSingleBond * sBond, TSingleBond * qBond) { //for bond's number BONDNUMBER in structure (BOND) and for query bond's number //QBONDNUMBER (QBOND) determines, if they may be accociated one with other. //QBC-array, for each query bond contains explicitly defined cyclic conditions: // 0-no cyclic condition is implemented // 1-query bond and corresponding structure bond must be a Chain // 2-query bond and corresponding structure bond must be a Ring //Function has 'TRUE' value if bonds can be matched, FALSE otherwise bool test1; int bT,qBT,bD,qBD; bool result=false; bT=sBond->tb; qBT=qBond->tb; bD=sBond->db; qBD=qBond->db; if (sBond->special>=3) sBond->special=sBond->special-3; if (qBond->special>=3) qBond->special=qBond->special-3; if (((qBond->special==1) && (bD>1)) || ((qBond->special==2) && (bD<2))) return result; //Explicitly defined cyclic conditions are violated if ((qBD>1) && (bD<2)) return result; //Implicitly defined cyclic conditions are violated} if (qBT==ANY_BOND) //Any bond in query { result=true; return result; }; if ((qBT==4) || (qBD==2) || (qBD==3)) //Aromatic bond { result=(bD==2) || (bD==3) || (bT==4); return result; }; if ((bD==2) || (bD==3)) { //non-Aromatic in query, but Aromatic in structure result=false; return result; }; test1=false; if ((qBT==5) && ((bT==1) || (bT==2) || (bT==5))) test1=true; //SINGLE/DOUBLE query bond if (bT==qBT) test1=true; //Type of bonds in structure and in query are equivalent} result=test1; return result; }; int TSingleBond::bondConversion() { //generate a code for bond's nuber BNB in array BOND. It is took into consi- //deration bond type and cycle size int b1,b2; if (tb > 4) b1=0; else b1=tb; b2=7; //for other Switch value switch (db) { case 0 : b2=5; break; case 1 : b2=0; break; case 2 : b1=4; b2=1; break; case 3 : b1=4; b2=2; break; case 4 : b2=3; break; case 5 : b2=4; break; case 6 : b2=6; break; }; b2=b2 << 2; b2=b2+b1; return b2; }; void TSingleBond::bondCopy(TSingleBond * source) { this->at[0]=source->at[0]; this->at[1]=source->at[1]; this->bstereo=source->bstereo; this->db=source->db; this->special=source->special; this->tb=source->tb; }; //****************************************************************** // TSimpleMolecule //------------------------------------------------------------------ class TSimpleMolecule { protected: std::vector fAtom; std::vector fBond; public: std::ostream * refofs; virtual ~TSimpleMolecule() { clear(); }; TSimpleMolecule() { refofs=NULL; }; int nAtoms(); int nBonds(); TSingleAtom * getAtom(int index); TSingleBond * getBond(int index); void defineAtomConn(); void defineBondConn(neigbourlist & bondConnection); void newB(neigbourlist & bk, int bnum, int anum, int & total, int * e, int * e1); void singleVawe(neigbourlist & bk, std::vector & alreadyDefined, std::vector & prevBond, std::vector & prevAtom, int & nPrev, std::vector & curBond, std::vector & curAtom); void vaweBond(int bondN, neigbourlist & bk, int & ringSize, std::vector & bondList); void allAboutCycles(); void redraw(const std::vectorlistAtomClean, const std::vectorlistBondClean, int & atomClean, int & bondClean, int spn, int sCHA1, int sCHB1, bool iOPT7); void clear(); void readOBMol(OBMol * pmol); void readConnectionMatrix(const std::vectoriA1, const std::vectoriA2, int nAtoms, int nBonds); void readConnectionMatrix(const std::vectoriA1, const std::vectoriA2, const std::vectorrx, const std::vectorry, int nAtoms, int nBonds); void redrawMolecule(); void getMolfile(std::ostream & data); bool checkOverlapped(); double averageBondLength(); void flipSmall(int cHB); int listarSize(); bool makeFragment(int& n, std::vector& list, int aT, int aTEx); void moleculeCopy(TSimpleMolecule & source); int correctOverlapped(); void normalizeCoordinates(double aveBL); void makeEquivalentList(std::vector& equivalenceList, bool isTopologyOnly); void bondEnlarge(int bN); void deleteAtom(int index); void deleteBond(int index); void addAtom(TSingleAtom * sa); void addAtom(int na, int charge, double rx, double ry); void addBond(TSingleBond * sb); void addBond(int tb, int at1, int at2); void setCoordinatesString(string value); void unitVector(int aN, double& xV, double& yV); void bondUnitVector(int bn, double& xv, double& yv); double bondLength(int index); int singleAtomicDescriptor(int aNumber,int bNumber, bool useEnumerator); int hasOverlapped(double delta, bool findFirst); int getNH(int atomNo); private: bool aromatic(int cycleSize, const std::vector bondList, std::vector& arom); void twoAtomUnitVector(int na1, int na2, double & xv, double & yv, const std::vectoratomDefine); void defC(int& currNumDef, int baseCycle, int atomClean, std::vector& cycleDefine, std::vector& atomDefine, std::vector& atomCycle, std::vector& cycleAddress, std::vector& cycleSize, std::vector& dsATN, std::vector& dsTP, std::vector& dsSC, std::vector& dsNA1, std::vector& dsNA2); void defA(int& currNumDef, int atomClean, int sPN, int baseCycle, std::vector& atomDefine, const std::vector listAtomClean, std::vector& cycleDefine, std::vector& cycleSize, std::vector& cycleAddress, std::vector& atomCycle, std::vector& dsATN, std::vector& dsTP, std::vector& dsNA1, std::vector& dsNA2); void canonizeCycle(int ringSize, std::vector & bondList); //Chain rotate members double atomDistanceMetric(int an); bool bondsOverlapped(int bN1, int bN2, double delta); int fragmentSecond(int sphere, int att, int secAt, const std::vector a, const std::vector b, const neigbourlist bk, std::vector& wSphere); bool threeBondResolve(int an, int bondExcluded, double& xv, double& yv, neigbourlist* bkExt); bool unitVectorCoincident(int aN, double xV, double yV); }; int TSimpleMolecule::getNH(int atomNo) { int result=0; TSingleAtom * atom; int i,n; if (atomNo >= nAtoms()) return result; atom=getAtom(atomNo); result=atom->nv; result=result-(atom->currvalence)+(atom->nc*TSingleAtom::chargeDeltaValency(atom->na))-(atom->rl); if (result < 0) result=0; if (atom->nb > 0) for (i=0; inb; i++) { n=atom->ac[i]; if (getAtom(n)->na == 1) result=result+1; }; return result; }; int TSimpleMolecule::singleAtomicDescriptor(int aNumber,int bNumber, bool useEnumerator) { //Bond BNumber has to have type=9 or 10 int result=0; int an [4]; int j,m,k; double x[3]; double y[3]; double r,r1,r2; bool testBad; double tn [2]; bool rsn; bool isInvert; if ((getAtom(aNumber)->nb < 3) || (getAtom(aNumber)->nb > 4)) return result; for (j=0; jnb; j++) an[j]=getAtom(aNumber)->ac[j]; if (useEnumerator) { //fragIndex as main base for (j=0; j<(getAtom(aNumber)->nb-1); j++) for (m=j+1; mnb; m++) if (getAtom(an[j])->fragIndex > getAtom(an[m])->fragIndex) { k=an[j]; an[j]=an[m]; an[m]=k; }; } else { for (j=0; j<(getAtom(aNumber)->nb-1); j++) for (m=j+1; mnb; m++) if (an[j] > an[m]) { k=an[j]; an[j]=an[m]; an[m]=k; }; }; // return 1; for (j=0; j<3; j++) { k=an[j]; x[j]=getAtom(k)->rx-getAtom(aNumber)->rx; y[j]=getAtom(k)->ry-getAtom(aNumber)->ry; }; isInvert=true; if (getAtom(aNumber)->nb == 4) if (getBond(bNumber)->at[1] != an[3]) { isInvert=false; for (j=0; j<3; j++) if (an[j] == getBond(bNumber)->at[1]) { k=an[3]; x[j]=getAtom(k)->rx-getAtom(aNumber)->rx; y[j]=getAtom(k)->ry-getAtom(aNumber)->ry; }; }; testBad=false; for (j=0; j<3; j++) { r=sqrt(x[j]*x[j]+y[j]*y[j]); if (r == 0) testBad=true; else { x[j]=x[j]/r; y[j]=y[j]/r; }; }; // return 1; if (! testBad) { for (j=0; j<2; j++) //determination of the sign of rotation { r1=x[0]*y[j+1]-y[0]*x[j+1]; //Sin r2=x[0]*x[j+1]+y[0]*y[j+1]; //Cos if (r1 < 0) r2=-2-r2; // 0 degress = +1; Pi/2 degrees = 0; Pi degrees = -1; 3*Pi/2 degrees = -2; 2*Pi degrees = -3 tn[j]=r2; }; rsn=( tn[0] > tn[1]); //return 1; //Analizing cases.... if (getBond(bNumber)->tb == 10) rsn=! rsn; if (getAtom(aNumber)->nb == 4) if (isInvert) rsn=! rsn; if (tn[0] != tn[1]) { if (rsn) result=1; else result=2; }; }; return result; }; void TSimpleMolecule::bondUnitVector(int bn, double& xv, double& yv) { //for bon's number BN in the structure, described by ATOM, BOND, CONN calculates //the unit vector (Xv,Yv on output). The vector is best direction to add new fra- //gment to current structure. The procedure is used by TEMPLATE and MAKEPOLI} double si,r1,r2,r3,s1,s2,s3,s4,x1,y1,y2; int na1,na2,i; na1=getBond(bn)->at[0]; na2=getBond(bn)->at[1]; s1=getAtom(na1)->rx; s2=getAtom(na1)->ry; s3=getAtom(na2)->rx; s4=getAtom(na2)->ry; r1=s1-s3; r2=s2-s4; r3=sqrt(r1*r1+r2*r2); r1=r1/r3; //COS r2=r2/r3; //SIN si=0; for (i=0; inb; i++) if (getAtom(na1)->ac[i] != na2) { x1=getAtom(getAtom(na1)->ac[i])->rx-s1; y1=getAtom(getAtom(na1)->ac[i])->ry-s2; y2=x1*r2-y1*r1; if (y2 != 0) si=si+y2/abs(y2); } for (i=0; inb; i++) if (getAtom(na2)->ac[i] != na1) { x1=getAtom(getAtom(na2)->ac[i])->rx-s3; y1=getAtom(getAtom(na2)->ac[i])->ry-s4; y2=x1*r2-y1*r1; if (y2 != 0) si=si+y2/abs(y2); } if (si != 0) si=si/abs(si); else si=1; xv=-r2*si; yv=r1*si; } bool TSimpleMolecule::threeBondResolve(int an, int bondExcluded, double& xv, double& yv, neigbourlist* bkExt) { //Addition from 16 April 2006 int bondNoList [3]; bool result=false; double centerX[3]; double centerY[3]; int nBondNo; neigbourlist* bk; std::vectorbondList(listarSize()); std::vector* blStore=NULL; bool testBad,testOK; int m,j,n1,n2,n,k; int rs; double dist,x,y,minDist; int i; bool test; int at; double r1,r2,s1,s3; if (bkExt != NULL) bk=bkExt; else { bk=(neigbourlist *)malloc((CONNMAX+1)*NBONDSMAX*4); defineBondConn(*bk); }; nBondNo=0; testBad=false; testOK=true; for (i=0; i<(*bk)[an].nb; i++) { n=(*bk)[an].adjusted[i]; if (n != bondExcluded) { vaweBond(n,*bk,rs,bondList); if (rs > 0) { //I have to analize bondList to determine second cycle to exclude adamanthane if (blStore == NULL) { //Save blStore= new std::vector(rs); for (j=0; jsize(); j++) (*blStore)[j]=bondList[j]; } else { m=0; for (j=0; jsize(); j++) { test=false; for (k=0; k 1) && (m < blStore->size())) { testOK=false; }; }; //center determination centerX[nBondNo]=0; centerY[nBondNo]=0; for (j=0; jat[0]; n2=getBond(m)->at[1]; centerX[nBondNo]=centerX[nBondNo]+getAtom(n1)->rx+getAtom(n2)->rx; centerY[nBondNo]=centerY[nBondNo]+getAtom(n1)->ry+getAtom(n2)->ry; }; centerX[nBondNo]=centerX[nBondNo]/(2*rs); centerY[nBondNo]=centerY[nBondNo]/(2*rs); bondNoList[nBondNo]=n; nBondNo++; }; };// else testBad:=true; if ((nBondNo == 3) || (testBad)) break; }; if (nBondNo < 2) testBad=true; if ((! testBad) && testOK) { dist=0; if (nBondNo == 2) { //single bond, attached to ring. Re-definition center.... x=0; y=0; for (i=0; irx; y=y+getAtom(i)->ry; }; x=x/nAtoms(); y=y/nAtoms(); for (i=0; iat[0]; if (at == an) at=getBond(bondNoList[i])->at[1]; r1=getAtom(at)->rx-getAtom(an)->rx; r2=getAtom(at)->ry-getAtom(an)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 == 0) testBad=true; else { r1=r1/s1; r2=r2/s1; x=-r1; y=-r2; minDist=1000000000; for (j=0; jrx-centerX[j]); r2=(y*s1+getAtom(an)->ry-centerY[j]); s3=sqrt(r1*r1+r2*r2); if (s3 < minDist) minDist=s3; }; if (minDist > dist) { dist=minDist; xv=x; yv=y; }; }; }; if (! testBad) result=true; }; //end addition from 16 April 2006 if (bkExt == NULL) free(bk); if (blStore != NULL) delete(blStore); return result; }; bool TSimpleMolecule::unitVectorCoincident(int aN, double xV, double yV) { bool result=false; int i, aT; double r1,r2,s1; for (i=0; inb; i++) { aT=getAtom(aN)->ac[i]; r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 > 0.00001) { r1=r1/s1; r2=r2/s1; if ((abs(r1-xV) < 0.1) && (abs(r2-yV) < 0.1)) result=true; }; if (result) break; }; return result; }; void TSimpleMolecule::unitVector(int aN, double& xV, double& yV) { //For atom's number AN in the array ATOM (the same structure is described with //bond's array BOND and bond-connection matrix invariants CONN) unit vector //is calculated (XV, YV) on output. Unit vector shows the best direction to //make new bond} double sc[4] = {0,1/2,1.7320508/2,1}; double cc[4] = {1,1.7320508/2,1/2,0}; double sQ3=sqrt(3.0)/2.0; double si,r1,r2,r3,r4,s1,s2,s3,s4,fi; double xm[3]; //Initial dimensions 1..3 double ym[3]; //Initial dimensions 1..3 int i,i1,i2; int nB1,aT,aT1; bool mK[CONNMAX]; bool test; nB1=1; if (getAtom(aN)->nb == 0) { xV=1; yV=0; //if no atoms connected-horizontal unit vector return; } else if (getAtom(aN)->nb == 1) //single atom connected-start calculations} { aT=getAtom(aN)->ac[0]; r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 == 0) { xV=0.3826834; yV=-0.9238795; return; } r1=r1/s1; r2=r2/s1; nB1=getAtom(aT)->nb; si=1; if (nB1 > 1) { aT1=getAtom(aT)->ac[0]; if (aT1 == aN) aT1=getAtom(aT)->ac[1]; r3=getAtom(aT1)->rx-getAtom(aN)->rx; r4=getAtom(aT1)->ry-getAtom(aN)->ry; s2=sqrt(r3*r3+r4*r4); if (s2 == 0) s2=1; r3=r3/s2; r4=r4/s2; si=r2*r3-r4*r1; if (si != 0) si=si/abs(si); else si=1; } s3=sqrt(3.0)/2.0; s4=-0.5; xV=r1*s4-r2*si*s3; //single atom connected-unit vector at 2Pi/3 angle yV=r2*s4+r1*si*s3; } else //multiply connection case { xV=0; yV=0; for (i=0; inb; i++) mK[i]=true; if (getAtom(aN)->nb == 3) { //three-bond connection-search for projec. of tetrahedron if (threeBondResolve(aN,-1,xV,yV,NULL)) { return; }; for (i=0; i<3; i++) { aT=getAtom(aN)->ac[i]; xm[i]=getAtom(aT)->rx-getAtom(aN)->rx; ym[i]=getAtom(aT)->ry-getAtom(aN)->ry; r1=sqrt(xm[i]*xm[i]+ym[i]*ym[i]); xm[i]=xm[i]/r1; ym[i]=ym[i]/r1; } i=0; do //Search for bond, which make 30 degree angle with another { i1=1; i2=2; switch (i) { case 0: { i1=1; i2=2; break; } case 1: { i1=0; i2=2; break; } case 2: //Else value - only three { i1=0; i2=1; break; } } s1=xm[i1]*xm[i2]+ym[i1]*ym[i2]; s2=xm[i]*xm[i1]+ym[i]*ym[i1]; s3=xm[i]*xm[i2]+ym[i]*ym[i2]; test=false; if (abs(s1+0.5) < 0.05) //two bonds form 120 degrees angle { test=(abs(s2-sQ3)<0.05) || (abs(s3-sQ3)<0.05); if (! test) test=((abs(s2) < 0.05) && (abs(s3+sQ3)<0.05)) || ((abs(s3)<0.05) && (abs(s2+sQ3)<0.05)); } i++; } while (! (test || (i == 3))); if (test) mK[i-1]=false; //In the original Java code without -1 } for (i=0; inb; i++) if (mK[i]) { aT=getAtom(aN)->ac[i]; r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 < 0.05) s1=1; r1=r1/s1; r2=r2/s1; xV=xV-r1; yV=yV-r2; } r1=sqrt(xV*xV+yV*yV); if (r1 > 0.05) { //unit vector may be calculated from current connection xV=xV/r1; yV=yV/r1; } else { //impossible to calculate unit vector-more definitions if (getAtom(aN)->nb == 2) //linear existing connection-Pi/2 angle { aT=getAtom(aN)->ac[0]; r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 < 0.05) { xV=0.3826834; yV=-0.9238795; return; } r1=r1/s1; r2=r2/s1; xV=-r2; yV=-r1; } else if (getAtom(aN)->nb == 3) //3-bonds connection - projection of tetrahedrone is calculated { //Which of bonds has the horizontal direction r3=100000; for (i=0; i<3; i++) { aT=getAtom(aN)->ac[i]; r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 < 0.05) { xV=0.3826834; yV=-0.9238795; return; } r1=abs(r1/s1); r2=abs(r2/s1); if (r1 < r3) { r3=r1; nB1=i; } if (r2 < r3) { r3=r2; nB1=i; } } aT=getAtom(aN)->ac[nB1]; //Horizontal or vertical bond found} r1=getAtom(aT)->rx-getAtom(aN)->rx; r2=getAtom(aT)->ry-getAtom(aN)->ry; s1=sqrt(r1*r1+r2*r2); r1=r1/s1; r2=r2/s1; xV= r1*cos(150*PI/180)+r2*sin(150*PI/180); yV=-r1*sin(150*PI/180)+r2*cos(150*PI/180); } else { xV=sqrt(2.0)/2.0; //other case - Pi/4 to horizont angle yV=-xV; } } //checking if coincided xV and uV with existing bonds if (unitVectorCoincident(aN,xV,yV)) { fi=15*PI/180.0; r3=cos(fi); r4=sin(fi); r1= xV*r3+yV*r4; r2=-xV*r4+yV*r3; if (unitVectorCoincident(aN,r1,r2)) { r1=xV*r3-yV*r4; r2=xV*r4+yV*r3; }; if (unitVectorCoincident(aN,r1,r2)) { fi=7.5*PI/180.0; r3=cos(fi); r4=sin(fi); r1= xV*r3+yV*r4; r2=-xV*r4+yV*r3; if (unitVectorCoincident(aN,r1,r2)) { r1=xV*r3-yV*r4; r2=xV*r4+yV*r3; }; }; xV=r1; yV=r2; }; }; //Correction of angles, closed to 0, 30, 60, 90 degrees to exact values if (getAtom(aN)->nb < 6) for (i=0; i<4; i++) if ((abs(abs(xV)-sc[i])<0.04) && (abs(abs(yV)-cc[i]) < 0.04)) { if (xV < 0) xV=-sc[i]; else xV=sc[i]; if (yV < 0) yV=-cc[i]; else yV=cc[i]; } } void TSimpleMolecule::clear() { for (int i=0; ina=101; else if (s == "2") sa->na=102; else if (s == "3") sa->na=103; else if (s == "A") sa->na=ANY_ATOM; else if (s == "E") { sa->na=ANY_ATOM; sa->special=1; } else sa->na=6; //Atom.positionofAtom(s); n=n+1; s=value.substr(n,4); n=n+4; k=strtol(s.c_str(),NULL,10); sa->rx=(double)k/10000.0; s=value.substr(n,4); n=n+4; k=strtol(s.c_str(),NULL,10); sa->ry=(double)k/10000.0; addAtom(sa); }; s=value.substr(n,2); //2 symbols - number of bonds n=n+2; kk=strtol(s.c_str(),NULL,10); for (i=0; itb=ANY_BOND; //any bond s=value.substr(n,2); n=n+2; k=strtol(s.c_str(),NULL,10); sb->at[0]=k-1; s=value.substr(n,2); n=n+2; k=strtol(s.c_str(),NULL,10); sb->at[1]=k-1; addBond(sb); }; }; void TSimpleMolecule::addAtom(TSingleAtom * sa) { fAtom.push_back(sa); }; void TSimpleMolecule::addAtom(int na, int charge, double rx, double ry) { TSingleAtom * sa; sa=new TSingleAtom(); sa->na=na; sa->nc=charge; sa->rx=rx; sa->ry=ry; fAtom.push_back(sa); }; void TSimpleMolecule::addBond(TSingleBond * sb) { fBond.push_back(sb); }; void TSimpleMolecule::addBond(int tb, int at1, int at2) { TSingleBond * sb; sb=new TSingleBond(); sb->tb=tb; sb->at[0]=at1; sb->at[1]=at2; fBond.push_back(sb); }; void TSimpleMolecule::moleculeCopy(TSimpleMolecule & source) { TSingleAtom * sa; TSingleBond * sb; clear(); for (int i=0; iclone()); }; for (int i=0; iclone()); }; if (refofs == NULL) refofs=source.refofs; }; void TSimpleMolecule::deleteBond(int index) { std::vector tempBond(nBonds()); int i,n; n=0; //bond deletion for (i=0; i tempAtom(nAtoms()-1); std::vector tempBond(nBonds()); int i,n; n=0; //atom deletion for (i=0; iat[0] == index) || (getBond(i)->at[1] == index)) { delete(getBond(i)); fBond[i]=NULL; } else { if (getBond(i)->at[0] > index) getBond(i)->at[0]=getBond(i)->at[0]-1; if (getBond(i)->at[1] > index) getBond(i)->at[1]=getBond(i)->at[1]-1; tempBond[n]=getBond(i); n++; }; }; fBond.resize(n); for (i=0; iat[0]; k2=getBond(index)->at[1]; x=getAtom(k1)->rx-getAtom(k2)->rx; y=getAtom(k1)->ry-getAtom(k2)->ry; result=sqrt(x*x+y*y); return result; }; int TSimpleMolecule::listarSize() { int result=10; //Minimal vector size 10 if (nAtoms() > result) result=nAtoms(); if (nBonds() > result) result=nBonds(); return result; }; bool TSimpleMolecule::makeFragment(int& n, std::vector& list, int aT, int aTEx) { //Starting from atom's number AT in bond-connection matrix invariants array CONN //the all connected atom's (through one or more bonds) numbers are inserted into //the LIST array. Total number of such atoms is stored in N. If ATEX non-equal //zero, than atom with this number and all other, which is connected through //atom ATEX, don't include in LIST array. Boolean variable TEST has TRUE value //on output if between AT and ATEX exist cyclic bond, false otherwise} int i,j,k,l; bool test, test1; test=false; if ((nAtoms() ==0 ) || (aT < 0) || (aT >= nAtoms())) { n=0; list[0]=-1;; return test; } n=1; list[0]=aT; for (i=0; inb; i++) if (getAtom(aT)->ac[i] != aTEx) { list[n]=getAtom(aT)->ac[i]; n++; } if (n == 1) return test; //exit in AT non-connected atom k=0; do //iterational algorithm to define all} { for (i=0; inb; i++) { test1=false; l=getAtom(list[k])->ac[i]; if (l != aTEx) { for (j=0; j= 0) && (l < nAtoms())) { list[n]=l; n++; } } k=k+1; //atoms which are connected with AT} } while (kiA1, const std::vectoriA2, int nAtoms, int nBonds) { TSingleAtom * sa; TSingleBond * sb; int i; clear(); srand(30000); for (i=1; i<=nAtoms; i++) { sa=new TSingleAtom(); sa->na=6; sa->nv=hVal[sa->na]; sa->rx=(double)rand()/1000.0; sa->ry=(double)rand()/1000.0; fAtom.push_back(sa); }; for (i=0; iat[0]=iA1[i]; sb->at[1]=iA2[i]; sb->tb=1; fBond.push_back(sb); }; defineAtomConn(); allAboutCycles(); }; void TSimpleMolecule::readConnectionMatrix(const std::vectoriA1, const std::vectoriA2, const std::vectorrx, const std::vectorry, int nAtoms, int nBonds) { readConnectionMatrix(iA1,iA2,nAtoms,nBonds); for (int i=0; irx=rx[i]; getAtom(i)->ry=ry[i]; }; }; void TSimpleMolecule::normalizeCoordinates(double aveBL) { int i; double d,xMin,yMin; if (nAtoms() == 0) return; d=averageBondLength(); if ((d > 0) && (aveBL>0)) for (i=0; irx=getAtom(i)->rx*aveBL/d; getAtom(i)->ry=getAtom(i)->ry*aveBL/d; }; //Normalizing X and Y coordinates xMin=getAtom(0)->rx; yMin=getAtom(0)->ry; for (i=0; irx < xMin) xMin=getAtom(i)->rx; if (getAtom(i)->ry < yMin) yMin=getAtom(i)->ry; }; for (i=0; i< nAtoms(); i++) { getAtom(i)->rx=getAtom(i)->rx-xMin+aveBL; getAtom(i)->ry=getAtom(i)->ry-yMin+aveBL; }; }; void TSimpleMolecule::readOBMol(OBMol * pmol) { TSingleAtom * sa; TSingleBond * sb; OBAtom * atom; OBBond * bond; int na,nb,i; clear(); na=pmol->NumAtoms(); nb=pmol->NumBonds(); for (i=1; i<=na; i++) { atom=pmol->GetAtom(i); sa=new TSingleAtom(); sa->na=atom->GetAtomicNum(); sa->nc=atom->GetFormalCharge(); sa->rl=atom->GetSpinMultiplicity(); sa->rx=atom->GetX(); sa->ry=atom->GetY(); fAtom.push_back(sa); }; for (i=0; iGetBond(i); sb=new TSingleBond(); sb->at[0]=bond->GetBeginAtomIdx()-1; sb->at[1]=bond->GetEndAtomIdx()-1; sb->tb=bond->GetBondOrder(); // if (bond->IsUp()) sb->tb=9; // if (bond->IsDown()) sb->tb=10; if (bond->IsWedge()) sb->tb=9; if (bond->IsHash()) sb->tb=10; fBond.push_back(sb); }; defineAtomConn(); allAboutCycles(); }; int TSimpleMolecule::nAtoms() { return fAtom.size(); }; int TSimpleMolecule::nBonds() { return fBond.size(); }; TSingleAtom * TSimpleMolecule::getAtom(int index) { return (TSingleAtom *)fAtom.at(index); }; TSingleBond * TSimpleMolecule::getBond(int index) { return (TSingleBond *)fBond.at(index); }; void TSimpleMolecule::defineAtomConn() { //for each atom returns list of adjusted atoms in atomConnection int i,n1, n2; TSingleAtom * sa; for (i=0; inb=0; getAtom(i)->currvalence=0; }; for (i=0; iat[0]; n2=getBond(i)->at[1]; sa=getAtom(n1); sa->ac[sa->nb]=n2; sa->nb++; sa->currvalence=sa->currvalence+getBond(i)->getValence(); sa=getAtom(n2); sa->ac[sa->nb]=n1; sa->nb++; sa->currvalence=sa->currvalence+getBond(i)->getValence(); }; }; void TSimpleMolecule::defineBondConn(neigbourlist & bondConnection) { //for each atom returns list of adjusted bonds in bondConnection int i,n1, n2; for (i=0; i<=nAtoms(); i++) bondConnection[i].nb=0; for (i=0; iat[0]; n2=getBond(i)->at[1]; bondConnection[n1].adjusted[bondConnection[n1].nb]=i; bondConnection[n1].nb++; bondConnection[n2].adjusted[bondConnection[n2].nb]=i; bondConnection[n2].nb++; }; }; void TSimpleMolecule::newB(neigbourlist & bk, int bnum, int anum, int & total, int * e, int * e1) { /*for atom's number ANUM calculates the bond's list, which are connected with the ANUM, excluding bond BNUM. The bond's list is inserted into array L, TOTAL-number of components in the array. Array L1 contains atom's number which is connected with atom ANUM through corresponding bond in the array L.*/ int n,i; total=0; for (i=0; iat[0] == anum) e1[total]=getBond(n)->at[1]; else e1[total]=getBond(n)->at[0]; total++; }; }; void TSimpleMolecule::singleVawe(neigbourlist & bk, std::vector & alreadyDefined, std::vector & prevBond, std::vector & prevAtom, int & nPrev, std::vector & curBond, std::vector & curAtom) { /* a vawe algorithm is used to calculate the neighbour sphere. PREVBOND and PREVATOM contain on input the bond's and atom's list correspondingly, the atom has been took into consideration at previous step. NPREV-number of components in PREVBOND and PREVATOM on input. ALREADYDEFINED is the global array for procedure, which calls the SINGLEVAWE. It contains for each bond's number either zero, if the bond has not been took into consideration yet or bond's number from previous neighbour sphere. The subroutine makes next: 1) calculates the new neighbour sphere for each bond from PREVBOND. 2) if some bonds were not defined, they are labelled in ALREADYDEFINED, they numbers being stored into PREVBOND and PREVATOM. So, on output the arrays contain information for new neighbour sphere. 3) On output NPrev is changed so new number of component in PREVBOND is lighted} */ int e[CONNMAX]; int e1[CONNMAX]; int i,j,ncur; int n; ncur=0; n=0; for (i=0; i & bondList) { /*The procedure for bond's number BONDN determines, whether or not the bond belongs to ring (RINGSIZE=0 if not ring bond and RINGSIZE=number of atoms in the smallest ring, to which the bond is assigned). If the bond belongs to ring, array BONDLIST contains on output the bond's numbers, which are also assigned to the same ring */ int i,j,k,aA,i1,i2; std::vectorpA(NBONDSMAX); std::vectorpB(NBONDSMAX); std::vectoroD(NBONDSMAX); std::vectorcurBond(NBONDSMAX); std::vectorcurAtom(NBONDSMAX); bool test; int nP; for (j=0; jat[0];//pA.setValue(1,fBond.getAT(bondN,1)); aA=getBond(bondN)->at[1];//fBond.getAT(bondN,2); k=0; test=false; while (! ((nP==0) || test)) //recursion { ringSize++; singleVawe(bk,oD,pB,pA,nP,curBond,curAtom); //new neighbour sphere generation} test=false; if (nP>0) for (j=0; j i2) { bondList[i]=i2; bondList[j]=i1; }; }; } else ringSize=0; }; //This method must be called ONLY from AllAboutCycles bool TSimpleMolecule::aromatic(int cycleSize, const std::vector bondList, std::vector& arom) { /*for a cycle with the dimension of CYCLESIZE determines, whether or not the cycle is aromatic. BONDLIST contains the bond's numbers, which are belongs to the cycle. AROM-global array for the structure under study, for each bond in the structure contains label, whether the bond is aromatic or not. On out- put function AROMATIC contains TRUE if the ring is aromatic, FALSE otherwise */ bool test; int i,j,k,n,n1,m; int doubleDetected[11]; int atomDetected[11]; bool result=false; if ((cycleSize<5) || (cycleSize>6)) return result; //procedure define aromatic only for 5 or 6 membered cycles} //Simple Test} n=0; for (i=0; itb == 1) && (arom[k]==0)) doubleDetected[i]=0; else if ((getBond(k)->tb == 2) || (getBond(k)->tb == 4) || (arom[k]>0)) { n=n+1; //number of double or aromatic bonds calculation} doubleDetected[i]=1; } else return result; }; if (cycleSize==6) //6-membered ring} { if (n>=3) { result=true; if (n<=4) for (i=0; iat[0] == getBond(bondList[j])->at[0]) || (getBond(bondList[i])->at[0] == getBond(bondList[j])->at[1]) || (getBond(bondList[i])->at[1] == getBond(bondList[j])->at[0]) || (getBond(bondList[i])->at[1] == getBond(bondList[j])->at[1]); if (test) result=false; if (! result) break; }; if (! result) break; }; }; return result; }; if (n<2) return result; //5-membered ring m=0; for (i=0; iat[0]; m++; atomDetected[m]=getBond(bondList[i])->at[1]; m++; }; n=-1; if (m>0) for (i=0; i<(m-1); i++) for (j=i+1; j=0) { n1=-1; for (i=0; i<(m-1); i++) if (i != n) for (j=i+1; j=0) return result; }; if (n < 0) //No common atoms... { result=m==4; return result; }; test=false; i=0; j=getAtom(atomDetected[n])->na; for (i=0; inc < 0); //cyclopentadienyl checking} result=test; return result; }; void TSimpleMolecule::allAboutCycles() { /* for each bond in the structure cyclic conditions are calculated and stored into the BOND[I].DB variables. Possible values: 0 - chain; 1 - chain, appended to cycle; 2 - aromatic, 5-membered ring; 3 - aromatic, 6-membered ring; 4...more = non-aromatic ring size+1} */ std::vectorbondTested(NBONDSMAX); std::vectorbondList(NBONDSMAX); std::vectorar(NBONDSMAX); std::vectorrSize(NBONDSMAX); std::vectorar1(NBONDSMAX); std::vectorcycleDescription(3*NBONDSMAX); std::vectorcycleAddress(NBONDSMAX); int i,n,j,k; int cycleSize; neigbourlist * bk; bk=(neigbourlist *)malloc((1+CONNMAX)*NBONDSMAX*4); if (nBonds() == 0) { free(bk); return; } defineBondConn(*bk); //initial values for MainList-number of bonds, connected to each atom and their numbers in array BOND for (i=0; idb=0; ar[i]=0;; bondTested[i]=0; }; n=0; cycleAddress[0]=0; for (i=0; i0) //Yes, cyclic { canonizeCycle(cycleSize,bondList); n++; cycleAddress[n]=cycleAddress[n-1]+cycleSize; //store cycle definitions rSize[n-1]=cycleSize; for (j=0; j0) { j=1; while (j != 0) { j=0; for (i=0; idb==0) || (kdb)) getBond(bondList[j])->db=k; }; }; for (i=0; idb=2; if ((cycleSize==6) && (getBond(bondList[j])->db != 2)) getBond(bondList[j])->db=3; }; }; }; free(bk); }; void TSimpleMolecule::redrawMolecule() { int i; int atomClean; int bondClean; std::vectorlistAtomClean(nAtoms()); std::vectorlistBondClean(nBonds()); if (nAtoms()==0) return; for (i=0; iatomDefine) { /*Using atom's numbers NA1 and NA2 in the array ATOMS calculates the direction for bond between NA1 and NA2 (XV,YV on output). At this direction new substitutor may be added with optimal place.*/ double si,r1,r2,r3,s1,s2,s3,s4,x1,y1,y2; int i,k; if ((getAtom(na1)->rx==getAtom(na2)->rx) && (getAtom(na1)->ry==getAtom(na2)->ry)) { xv=1; yv=1; return; }; s1=getAtom(na1)->rx; s2=getAtom(na1)->ry; s3=getAtom(na2)->rx; s4=getAtom(na2)->ry; r1=s1-s3; r2=s2-s4; r3=sqrt(r1*r1+r2*r2); r1=r1/r3; r2=r2/r3; si=0; for (i=0; inb; i++) { k=getAtom(na1)->ac[i]; if (k != na2) if (atomDefine[k] > 0) { x1=getAtom(k)->rx-s1; y1=getAtom(k)->ry-s2; y2=x1*r2-y1*r1; if (y2 != 0) si=si+y2/abs(y2); }; }; for (i=0; inb; i++) { k=getAtom(na2)->ac[i]; if (k != na1) if (atomDefine[k] > 0) { x1=getAtom(k)->rx-s3; y1=getAtom(k)->ry-s4; y2=x1*r2-y1*r1; if (y2 != 0) si=si+y2/abs(y2); }; }; if (si != 0) si=si/abs(si); else si=1; xv=-r2*si; yv=r1*si; }; void TSimpleMolecule::defC(int& currNumDef, int baseCycle, int atomClean, std::vector& cycleDefine, std::vector& atomDefine, std::vector& atomCycle, std::vector& cycleAddress, std::vector& cycleSize, std::vector& dsATN, std::vector& dsTP, std::vector& dsSC, std::vector& dsNA1, std::vector& dsNA2) { //The procedure create priority list formation for cleaning of molecule. Recur- // sive calls to the procedure are required together with DefA. After recursion // have been finished, the DEFINESEQUENCE array will be created. The procedure // analyze cyclic fragments int aA,cN,maxAtDef,i,j,atDef; bool test; test=true; while (test) { cN=-1; if ((baseCycle==0) || (currNumDef==atomClean)) return; maxAtDef=0; for (i=0; i0) atDef=atDef+1; if (atDef > maxAtDef) { // { search for cycles, for which maximal number of atoms were inserted in the priority list} maxAtDef=atDef; cN=i; } else if ((maxAtDef > 0) && (atDef==maxAtDef)) { // {search for minimal cycle size} if (cycleSize[i]=0) if (maxAtDef==cycleSize[cN]) { cycleDefine[cN]=1; cN=-1; }; // {CN-number of selected cycle with max.number of atoms, already defined, or with minimal size} if (cN>=0) { cycleDefine[cN]=1; //{Label, that CN-th cycle is inserted into priority list} test=(atomDefine[atomCycle[cycleAddress[cN]]]>0) && (atomDefine[atomCycle[cycleAddress[cN]+cycleSize[cN]-1]]==0); // {if first atom from cyclic fragment was inserted into list previously and last-no} while (! test) { // {enumeration of atom's sequence in cyclic fragment} aA=atomCycle[cycleAddress[cN]]; for (j=1; j0) && (atomDefine[atomCycle[cycleAddress[cN]+cycleSize[cN]-1]]==0); }; // {now first atom in the cycle definition list must be inserted into the priority list early, and the last atom-no} for (j=0; j& atomDefine, const std::vector listAtomClean, std::vector& cycleDefine, std::vector& cycleSize, std::vector& cycleAddress, std::vector& atomCycle, std::vector& dsATN, std::vector& dsTP, std::vector& dsNA1, std::vector& dsNA2) { //The procedure create priority list formation for cleaning of molecule. Recur- // sive calls to the procedure are required together with DefC. After recursion //have been finished, the DEFINESEQUENCE array will be created. The procedure //analyze chain fragments int i,j,k,rC; if (currNumDef==atomClean) return; for (i=0; inb > 0) for (j=0; jnb; j++) { k=getAtom(listAtomClean[i])->ac[j]; if (atomDefine[k]>0) { //if atom has a neighbour, which has already been inserted into priority // list, then it is inserted into the priority list} dsATN[currNumDef]=listAtomClean[i]; atomDefine[dsATN[currNumDef]]=1; dsTP[currNumDef]=1; //type of clean} dsNA1[currNumDef]=k; dsNA2[currNumDef]=-1; currNumDef++; //only one atom is added into priority list, then it is necessary to // analyze cycles by DEFC procedure!} return; }; }; if ((sPN<3) || (sPN==4)) //{next may not be implemented to CleanGroup command} { //{initializing priority list for clean of a new fragment} rC=0; j=100000; //minimal cycle size searching} if (baseCycle>0) for (i=0; i0) i=atomCycle[cycleAddress[rC]]; else { // {not found-first undefined atom is selected} i=0; while (atomDefine[listAtomClean[i]] != 0) i++; i=listAtomClean[i]; }; } else i=listAtomClean[atomClean-1]; //for group-first atom, from which group started, is selected} dsATN[currNumDef]=i; atomDefine[dsATN[currNumDef]]=1; dsTP[currNumDef]=0; //type of clean} dsNA1[currNumDef]=-1; dsNA2[currNumDef]=-1; currNumDef++; //Addition the atom selected to the list} }; void TSimpleMolecule::canonizeCycle(int ringSize, std::vector & bondList) { //Order of nonds in cycle description in so way, that bond with minimal number //is the first in this description. Then is coming bond, which is appended to //atom with maximal number, then next and so on std::vector bondUsed(ringSize); std::vector newBondList(ringSize); int i,j,n,m,currentAtom; n=bondList[0]; m=0; for (i=0; iat[0]; if (getBond(n)->at[1] > currentAtom) currentAtom=getBond(n)->at[1]; newBondList[0]=n; bondUsed[m]=1; n=1; for (i=1; iat[0] == currentAtom) || (getBond(m)->at[1] == currentAtom)) { bondUsed[j]=1; newBondList[n]=m; n++; if (getBond(m)->at[0] == currentAtom) currentAtom=getBond(m)->at[1]; else currentAtom=getBond(m)->at[0]; break; }; }; }; //copy new set for (i=0; i bondList(nBonds()); std::vector blStore(nBonds()); bool testBad,testOK; int m,j,n1,n2,n,k; int rs; double dist,x,y,minDist; int i; bool test,testStore; int at; double r1,r2,s1,s3; xv=0; yv=1; nBondNo=0; testBad=false; testOK=true; testStore=false; for (i=0; i 0) { //I have to analize bondList to determine second cycle to exclude adamanthane if (! testStore) { //Save blStore.clear(); for (j=0; j 1) && (m < blStore.size())) { testOK=false; }; }; //center determination centerX[nBondNo]=0; centerY[nBondNo]=0; for (j=0; jat[0]; n2=getBond(m)->at[1]; centerX[nBondNo]=centerX[nBondNo]+getAtom(n1)->rx+getAtom(n2)->rx; centerY[nBondNo]=centerY[nBondNo]+getAtom(n1)->ry+getAtom(n2)->ry; }; centerX[nBondNo]=centerX[nBondNo]/(2*rs); centerY[nBondNo]=centerY[nBondNo]/(2*rs); bondNoList[nBondNo]=n; nBondNo++; }; };// else testBad:=true; if ((nBondNo == 3) || (testBad)) break; }; if (nBondNo < 2) testBad=true; if ((! testBad) && testOK) { dist=0; if (nBondNo == 2) { //single bond, attached to ring. Re-definition center.... x=0; y=0; for (i=0; irx; y=y+getAtom(i)->ry; }; x=x/(double)nAtoms(); y=y/(double)nAtoms(); for (i=0; iat[0]; if (at== an) getBond(bondNoList[i])->at[1]; r1=getAtom(at)->rx-getAtom(an)->rx; r2=getAtom(at)->ry-getAtom(an)->ry; s1=sqrt(r1*r1+r2*r2); if (s1 == 0) testBad=true; else { r1=r1/s1; r2=r2/s1; x=-r1; y=-r2; minDist=1000000000; for (j=0; jrx-centerX[j]); r2=(y*s1+getAtom(an)->ry-centerY[j]); s3=sqrt(r1*r1+r2*r2); if (s3 < minDist) minDist=s3; }; if (minDist > dist) { dist=minDist; xv=x; yv=y; }; }; }; if (! testBad) result=true; }; //end addition from 16 April 2006 return result; }; */ void TSimpleMolecule::redraw(const std::vectorlistAtomClean, const std::vectorlistBondClean, int & atomClean, int & bondClean, int spn, int sCHA1, int sCHB1, bool iOPT7) { std::vector dsATN(NBONDSMAX); std::vector dsTP(NBONDSMAX); std::vector dsSC(NBONDSMAX); std::vector dsNA1(NBONDSMAX); std::vector dsNA2(NBONDSMAX); /* the arrays are the priority list for clean. Minimal element of record has the maximal priority. Each element of record contains: ATN-atom's number in the ATOM array, coordinate of which must be calculated at current step. TP -type of calculation of coordinates for the atom ATN: =0 - simple put at any position =1 - calculation from coordinates of previously defined atom NA1. NA2 value hasn't effect in the case. =2 - for cyclic fragment, coordinates of all cyclic atoms must be cal- culated from the ones for atom NA1-base atom in cycle. This type means fragment: chain bond, attached to the cycle. NA2 value hasn't effect in the case. =3 - for cyclic fragment, coordinates of all cyclic atoms must be cal- culated from the ones for pair of atoms NA1 and NA2. The pair is connected with bond. This type means fragment in condenced-cyclic systems. =4 - for cyclic fragment, coordinates of all cyclic atoms must be cal- culated from the ones for pair of atoms NA1 and NA2. The pair is connected through more, then one bond. This type means fragment in polycyclic systems. AN1,AN2-auxiliary atom's numbers (see TP description). SC-cycle size minus number of already defined atoms. The variable is taken into consideration for TP=2-4. */ bool test; int cs; int add,nb,k,k1,k2,l,i,j,baseCycle,lx,ly; int currNumDef; int atomSecond; std::vector atomCycle(3*nBonds()); std::vector cycleDescription(3*nBonds()); std::vector cycleAddress(nBonds()); std::vector atomDefine(NBONDSMAX); std::vector cycleSize(NBONDSMAX); std::vector cycleDefine(NBONDSMAX); std::vector tempArray(NBONDSMAX); double r,cf,fi,ux,uy,ux1,uy1,ux2,uy2,uvX,uvY,c,s; double xCenterOld,yCenterOld,xCenterNew,yCenterNew,bondLengthOld,bondLengthNew; neigbourlist bk; int n,n1,n2; double r1; bool isCycle,isChainFour; int mm1,mm2,mAny,bnEx; //Lx,Ly,Button:integer; if ((atomClean<1) || (bondClean==0)) return; defineAtomConn(); allAboutCycles(); test=true; defineBondConn(bk); //{Start clean for LISTATOMCLEAN and LISTBONDCLEAN atoms and bonds} cycleAddress[0]=0; baseCycle=0; cs=0; ux=0; uy=0; uvX=0; uvY=0; atomSecond=0; for (i=0; i0) { canonizeCycle(cs,atomDefine); test=false; for (j=0; jat[0]==getBond(j)->at[0]) || (getBond(k)->at[0]==getBond(j)->at[1])) atomCycle[cycleAddress[i]+0]=getBond(k)->at[0]; else atomCycle[cycleAddress[i]+0]=getBond(k)->at[1]; n=atomCycle[cycleAddress[i]+0]; //previoulsly putted atom for (j=1; jat[0]==n) atomCycle[cycleAddress[i]+j]=getBond(k)->at[1]; else atomCycle[cycleAddress[i]+j]=getBond(k)->at[0]; n=atomCycle[cycleAddress[i]+j]; }; }; //calculation of the coordinates of fragment's center and scaling factor} if ((spn<3) || (spn==4)) { xCenterOld=0; yCenterOld=0; bondLengthOld=0; if (spn == 4) { for (i=0; irx; yCenterOld=yCenterOld+getAtom(i)->ry; n++; }; xCenterOld=xCenterOld/(double)n; yCenterOld=yCenterOld/(double)n; for (i=0; ibondLength(i); if (r < bondLengthOld) bondLengthOld=n; r1=r1+r; n++; }; r1=r1/n; if (5*bondLengthOld < r1) bondLengthOld=r1; } else { for (i=0; irx; yCenterOld=yCenterOld+getAtom(k)->ry; }; xCenterOld=xCenterOld/(double)atomClean; yCenterOld=yCenterOld/(double)atomClean; for (i=0; ibondLength(k); }; bondLengthOld=bondLengthOld/(double)bondClean; }; if (bondLengthOld<0.01) bondLengthOld=1; } else { xCenterOld=getAtom(sCHA1)->rx; //{CHA1 - To atom connected, CHB1-splitting bond} yCenterOld=getAtom(sCHA1)->ry; lx=getBond(sCHB1)->at[0]; ly=getBond(sCHB1)->at[1]; if (ly == sCHA1) { lx=getBond(sCHB1)->at[1]; ly=getBond(sCHB1)->at[0]; }; atomSecond=ly; //for group it is necessary to calculate unit vector direction uvX=getAtom(ly)->rx-getAtom(lx)->rx; uvY=getAtom(ly)->ry-getAtom(lx)->ry; bondLengthOld=sqrt(uvX*uvX+uvY*uvY); if (bondLengthOld<0.01) bondLengthOld=1; uvX=uvX/bondLengthOld; uvY=uvY/bondLengthOld; }; for (i=0; irx=(xCenterOld+(getAtom(i)->rx-xCenterOld)/bondLengthOld); getAtom(i)->ry=(yCenterOld+(getAtom(i)->ry-yCenterOld)/bondLengthOld); }; }; i=0; if (atomClean>=1) while (irx=xCenterOld+(getAtom(dsATN[i])->rx-xCenterOld)/bondLengthOld; getAtom(dsATN[i])->ry=yCenterOld+(getAtom(dsATN[i])->ry-yCenterOld)/bondLengthOld; break; } case 1: case 2: if (atomDefine[dsATN[i]] == 0) { k1=dsNA1[i]; k=getAtom(k1)->nb; nb=0; ux=0; uy=0; ux1=0; uy1=0; //bond direction calculation} for (j=0; jac[j]]>0) { k2=getAtom(k1)->ac[j]; nb=nb+1; //number of already cleaned atoms} ux=ux+(getAtom(k1)->rx-getAtom(k2)->rx); uy=uy+(getAtom(k1)->ry-getAtom(k2)->ry); if (dsTP[i]==1) for (l=0; lnb; l++) if (atomDefine[getAtom(k2)->ac[l]]>0) { ux1=ux1+(getAtom(k2)->rx-getAtom(getAtom(k2)->ac[l])->rx); uy1=uy1+(getAtom(k2)->ry-getAtom(getAtom(k2)->ac[l])->rx); }; }; if (dsTP[i]==1) //chain fragment { if ((abs(ux) <= 0.00001) && (abs(uy) <= 0.00001)) { bnEx=-1; for (j=0; jat[0]; if (n1 == k1) n1=getBond(n)->at[1]; if (n1 == dsATN[i]) bnEx=n; if (bnEx >= 0) break; }; test= (bnEx >= 0); if (test) test=threeBondResolve(k1,bnEx,ux,uy,&bk); if (! test) { ux=ux1; uy=uy1; }; }; if ((ux==0) && (uy==0)) ux=1; fi=(k-nb-1)*PI/(double)k; //Addition for fluorine chain fragments... isChainFour=false; n=0; mm1=-1; mm2=-1; mAny=-1; if ((k == 4) && (getAtom(k1)->na == 6)) for (j=0; jnb; j++) { n1=getAtom(k1)->ac[j]; if (atomDefine[n1] == 0) { mAny=n1; if (getAtom(n1)->nb == 1) n++; else { if (mm1 == -1) mm1=n1; else mm2=n1; }; }; }; if ((mm1 == -1) && (n == 3)) { mm1=mAny; n--; }; isChainFour=(n == 2) && (mm1 >= 0); //End addition isCycle=false; if ((nb == 2) && ((k == 4) || (k == 5))) { n=0; for (j=0; jdb > 1) { n1=getBond(k2)->at[0]; if (n1 == k1) n1=getBond(k2)->at[1]; if (atomDefine[n1] != 0) n++; }; }; isCycle=(n == 2); }; if (isChainFour) { n=0; fi=PI/3; ux1= ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(mm1)->rx=getAtom(k1)->rx+ux1; //coordinates getAtom(mm1)->ry=getAtom(k1)->ry+uy1; atomDefine[mm1]=1; if (mm2 >= 0) { fi=-PI/3; ux1= ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(mm2)->rx=getAtom(k1)->rx+ux1; //coordinates getAtom(mm2)->ry=getAtom(k1)->ry+uy1; //coordinates atomDefine[mm2]=1; }; ux=0; uy=0; //New cosines.... for (j=0; jnb; j++) if (atomDefine[getAtom(k1)->ac[j]] > 0) { k2=getAtom(k1)->ac[j]; ux=ux+(getAtom(k1)->rx-getAtom(k2)->rx); uy=uy+(getAtom(k1)->ry-getAtom(k2)->ry); }; fi=PI/6; for (j=0; jnb; j++) { n1=getAtom(k1)->ac[j]; if (atomDefine[n1] == 0) { ux1= ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(n1)->rx=getAtom(k1)->rx+ux1; //coordinates} getAtom(n1)->ry=getAtom(k1)->ry+uy1; //coordinates} fi=-fi; atomDefine[n1]=1; }; }; } else if (isCycle) { if (k == 4) fi=PI/6; else fi=PI/3; ux1= ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(dsATN[i])->rx=getAtom(k1)->rx+ux1; //coordinates getAtom(dsATN[i])->ry=getAtom(k1)->ry+uy1; //coordinates atomDefine[dsATN[i]]=1; for (j=0; jnb; j++) { k2=getAtom(dsNA1[i])->ac[j]; if (atomDefine[k2] == 0) { ux1=ux*cos(fi)-uy*sin(fi); uy1=ux*sin(fi)+uy*cos(fi); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(k2)->rx=getAtom(k1)->rx+ux1; getAtom(k2)->ry=getAtom(k1)->ry+uy1; fi=0; atomDefine[k2]=1; }; }; } else { if (bk[dsNA1[i]].nb == 2) //two-connected fragment { //Search for triple bond or for two double-bonds n1=bk[dsNA1[i]].adjusted[0]; n2=bk[dsNA1[i]].adjusted[1]; if ((getBond(n1)->tb == 3) || (getBond(n2)->tb == 3)) k=3; else if ((getBond(n1)->tb == 2) && (getBond(n2)->tb == 2)) { if (getAtom(dsNA1[i])->na == 6) k=3; }; }; //Two-conncted fragment like pyrophosphate if ((k== 2) && (getAtom(dsNA1[i])->na != 6)) { n1=getAtom(dsNA1[i])->ac[0]; n2=getAtom(dsNA1[i])->ac[1]; if ((getAtom(n1)->nb >= 4) && (getAtom(n2)->nb >= 4) && (getAtom(n1)->na != 6) && (getAtom(n2)->na != 6)) k=3; }; //End pyrophosphate if (k==2) //120 degrees fragment} { if ((ux1==0) && (uy1==0)) ux1=1; fi=PI/3; cf=uy*ux1-ux*uy1; if (cf != 0) fi=fi*cf/abs(cf); }; ux1=ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; getAtom(dsATN[i])->rx=getAtom(k1)->rx+ux1; //coordinates getAtom(dsATN[i])->ry=getAtom(k1)->ry+uy1; //coordinates atomDefine[dsATN[i]]=1; }; } else //cyclic fragment} { if ((ux==0) && (uy==0)) ux=1; fi=(k-nb-2)*PI/(double)k; ux1=ux*cos(fi)+uy*sin(fi); uy1=-ux*sin(fi)+uy*cos(fi); cf=sqrt(ux1*ux1+uy1*uy1); if (cf != 0) { ux1=ux1/cf; uy1=uy1/cf; }; nb=dsSC[i]; cs=nb+1; cf=1/(2*sin(PI/(double)cs)); ux=getAtom(dsNA1[i])->rx+cf*ux1; uy=getAtom(dsNA1[i])->ry+cf*uy1; fi=2*PI/(double)cs; ux1=getAtom(dsNA1[i])->rx-ux; uy1=getAtom(dsNA1[i])->ry-uy; //coordinates of all atoms for the cycle under study are calculated} for (j=0; jrx=ux+ux1*cos((j+1)*fi)+uy1*sin((j+1)*fi); getAtom(dsATN[i+j-0])->ry=uy-ux1*sin((j+1)*fi)+uy1*cos((j+1)*fi); atomDefine[dsATN[i+j-0]]=1; }; i=i+nb-1; }; break; } else break; case 3: case 4: { twoAtomUnitVector(dsNA1[i],dsNA2[i],ux,uy,atomDefine); //calculation of an optimal side to add new fragment ux1=getAtom(dsNA1[i])->rx-getAtom(dsNA2[i])->rx; uy1=getAtom(dsNA1[i])->ry-getAtom(dsNA2[i])->ry; if (dsTP[i]==3) //angle calc. for condenced cycle} { nb=dsSC[i]; cs=nb+2; fi=sqrt(ux1*ux1+uy1*uy1); cf=fi/(2*sin(PI/(double)cs)/cos(PI/(double)cs)); add=0; } else //angle calc. for polycycle { r=sqrt(ux1*ux1+uy1*uy1); add=(int)ceil(r); nb=dsSC[i]; cs=nb+2+add; cf=r/(2*sin(PI*(nb+1)/(double)cs)); r=r/2.0; cf=sqrt(cf*cf-r*r); }; ux=(getAtom(dsNA1[i])->rx+getAtom(dsNA2[i])->rx)/2.0+ux*cf; uy=(getAtom(dsNA1[i])->ry+getAtom(dsNA2[i])->ry)/2.0+uy*cf; ux1=getAtom(dsNA1[i])->rx-ux; uy1=getAtom(dsNA1[i])->ry-uy; fi=2*PI/(double)cs; ux2=ux+ux1*cos((1.0+add)*fi)+uy1*sin((1.0+add)*fi); uy2=uy-ux1*sin((1.0+add)*fi)+uy1*cos((1.0+add)*fi); if ((abs(getAtom(dsNA2[i])->rx-ux2)<0.01) && (abs(getAtom(dsNA2[i])->ry-uy2)<0.01)) fi=-fi; //FAtom's coordinates calculation for (j=0; jrx=ux+ux1*cos((j+1)*fi)+uy1*sin((j+1)*fi); getAtom(dsATN[i+j-0])->ry=uy-ux1*sin((j+1)*fi)+uy1*cos((j+1)*fi); atomDefine[dsATN[i+j-0]]=1; } i=i+nb-1; break; } } i++; } bondLengthNew=0; //Rescaling and shift of structure for (i=0; irx; yCenterNew=yCenterNew+getAtom(listAtomClean[i])->ry; }; xCenterNew=xCenterNew/(double)atomClean; yCenterNew=yCenterNew/(double)atomClean; bondLengthNew=0.15*bondLengthOld; //IOPT[7]-controlles, whether or not the atom's shifts should be created, if coordinates of pair of atoms are identical if (spn==3) //Rescaling and shift coordinates for group (CODE=3) { xCenterNew=getAtom(sCHA1)->rx; yCenterNew=getAtom(sCHA1)->ry; ux=getAtom(atomSecond)->rx-getAtom(sCHA1)->rx; uy=getAtom(atomSecond)->ry-getAtom(sCHA1)->ry; r=sqrt(ux*ux+uy*uy); ux=ux/r; uy=uy/r; c=ux*uvX+uy*uvY; s=ux*uvY-uy*uvX; for (i=0; irx-xCenterNew; uy1=getAtom(listAtomClean[i])->ry-yCenterNew; getAtom(listAtomClean[i])->rx=c*ux1-s*uy1; getAtom(listAtomClean[i])->ry=s*ux1+c*uy1; }; xCenterNew=0; yCenterNew=0; }; if (spn != 4) for (i=0; irx=getAtom(listAtomClean[i])->rx-xCenterNew+xCenterOld; getAtom(listAtomClean[i])->ry=getAtom(listAtomClean[i])->ry-yCenterNew+yCenterOld; }; }; void TSimpleMolecule::getMolfile(std::ostream & data) { char buff[BUFF_SIZE]; TSingleAtom * sa; TSingleBond * sb; int charge,bondType,stereoType; data<nc) { case 1: charge = 3; break; case 2: charge = 2; break; case 3: charge = 1; break; case -1: charge = 5; break; case -2: charge = 6; break; case -3: charge = 7; break; default: charge=0; break; } snprintf(buff, BUFF_SIZE, "%10.4f%10.4f%10.4f %-3s%2d%3d%3d%3d%3d", sa->rx, sa->ry, 0.0, (aSymb[sa->na]).c_str(), 0,charge,0,0,0); data << buff << endl; }; for (int i=0; itb; stereoType=0; if (bondType == 9) { bondType=1; stereoType=1; } else if (bondType == 10) { bondType=1; stereoType=6; } else if (bondType == 11) { bondType=1; stereoType=4; }; snprintf(buff, BUFF_SIZE, "%3d%3d%3d%3d%3d%3d",(sb->at[0]+1), (sb->at[1]+1), bondType, stereoType, 0, 0); data << buff << endl; }; }; //******************************************************************************* //Below routines from ChainRotate-flipping acyclic bonds and enlarging bonds to get fine picture //******************************************************************************* double TSimpleMolecule::atomDistanceMetric(int an) { int i, n; double r, rr; double x1, y1, x2, y2, d, result; if (getAtom(an)->nb == 0) return 0; result=0.01; n=getAtom(an)->ac[0]; x1=getAtom(an)->rx-getAtom(n)->rx; y1=getAtom(an)->ry-getAtom(n)->ry; rr=sqrt(x1*x1+y1*y1); for (i=0; irx-getAtom(n)->rx; y2=getAtom(i)->ry-getAtom(n)->ry; d=rr*sqrt(x2*x2+y2*y2); if (d == 0) r=0; else r=(x1*x2+y1*y2)/d; if (r > 0) result=result+r; }; return result; }; double xDistPoint(double x1, double y1, double x2, double y2, double x0, double y0) { //The function finds distance between FAtom AN and bond BN (including sign). // The bond is treated as segment, not as straight line!} double d, r, r1, yMin, yMax, xMin, xMax, xx, yy, result; if (y1 < y2) { yMin=y1; yMax=y2; } else { yMin=y2; yMax=y1; }; xx=x1-x2; yy=y1-y2; r1=sqrt(xx*xx+yy*yy); yMin=yMin-0.1*r1; yMax=yMax+0.1*r1; d=y2-y1; if (abs(d) < 1E-8) { result=1E9; return result; }; if ((y0 > yMin) && (y0 < yMax)) { r=x1+(y0-y1)*(x2-x1)/d; if (x1 > x2) { xMin=x2; xMax=x1; } else { xMin=x1; xMax=x2; }; xMin=xMin-0.1*r1; xMax=xMax+0.1*r1; if (r < xMin) r=xMin; if (r > xMax) r=xMax; result=r-x0; } else result=1E9; return result; }; bool overlapped(double x1A, double y1A, double x2A, double y2A, double x1B, double y1B, double x2B, double y2B, double delta) { double a1, b1, c1, a2, b2, c2, r, cX, cY, x, y, r1, r2; double xMin, xMax, yMin, yMax; bool result=false; r=y2A-y1A; if (abs(r) > 1E-9) { a1=1/r; cY=-y1A/r; } else { a1=1E9; cY=-y1A*1E9; if (r < 0) { a1=-a1; cY=-cY; }; }; r=x2A-x1A; if (abs(r) > 1E-9) { b1=1/r; cX=x1A/r; } else { b1=1E9; cX=x1A*1E9; if (r < 0) { b1=-b1; cX=-cX; }; }; b1=-b1; c1=cX+cY; r=y2B-y1B; if (abs(r) > 1E-9) { a2=1/r; cY=-y1B/r; } else { a2=1E9; cY=-y1B*1E9; if (r < 0) { a2=-a2; cY=-cY; }; }; r=x2B-x1B; if (abs(r) > 1E-9) { b2=1/r; cX=x1B/r; } else { b2=1E9; cX=x1B*1E9; if (r < 0) { b2=-b2; cX=-cX; }; }; b2=-b2; c2=cX+cY; r1=b1*c2-b2*c1; r2=a1*b2-a2*b1; if (abs(r2) > 1E-9) y=r1/r2; else { y=1E9; if (r1 < 0) y=-y; }; r1=c1*a2-c2*a1; r2=a1*b2-a2*b1; if (abs(r2) > 1E-9) x=r1/r2; else { x=1E9; if (r1 < 0) x=-x; }; if (x1A < x2A) { xMin=x1A; xMax=x2A; } else { xMin=x2A; xMax=x1A; }; if (y1A < y2A) { yMin=y1A; yMax=y2A; } else { yMin=y2A; yMax=y1A; }; xMin=xMin-delta; xMax=xMax+delta; yMin=yMin-delta; yMax=yMax+delta; result=((x >= xMin) && (x <= xMax) && (y >= yMin) && (y <= yMax)); if (result) { if (x1B < x2B) { xMin=x1B; xMax=x2B; } else { xMin=x2B; xMax=x1B; }; if (y1B < y2B) { yMin=y1B; yMax=y2B; } else { yMin=y2B; yMax=y1B; }; xMin=xMin-delta; xMax=xMax+delta; yMin=yMin-delta; yMax=yMax+delta; result=((x >= xMin) && (x <= xMax) && (y >= yMin) && (y <= yMax)); }; if (! result) result=(abs(xDistPoint(x1A,y1A,x2A,y2A,x1B,y1B)) < delta); if (! result) result=(abs(xDistPoint(x1A,y1A,x2A,y2A,x2B,y2B)) < delta); if (! result) result=(abs(xDistPoint(x1B,y1B,x2B,y2B,x1A,y1A)) < delta); if (! result) result=(abs(xDistPoint(x1B,y1B,x2B,y2B,x2A,y2A)) < delta); return result; }; bool TSimpleMolecule::bondsOverlapped(int bN1, int bN2, double delta) { double x1A,y1A,x2A,y2A,x1B,y1B,x2B,y2B; bool result; x1A=getAtom(getBond(bN1)->at[0])->rx; y1A=getAtom(getBond(bN1)->at[0])->ry; x2A=getAtom(getBond(bN1)->at[1])->rx; y2A=getAtom(getBond(bN1)->at[1])->ry; x1B=getAtom(getBond(bN2)->at[0])->rx; y1B=getAtom(getBond(bN2)->at[0])->ry; x2B=getAtom(getBond(bN2)->at[1])->rx; y2B=getAtom(getBond(bN2)->at[1])->ry; result=false; if (((x1A>(x1B+delta)) && (x2A>(x1B+delta)) && (x1A>(x2B+delta)) && (x2A>(x2B+delta))) || ((x1A<(x1B-delta)) && (x2A<(x1B-delta)) && (x1A<(x2B-delta)) && (x2A<(x2B-delta))) || ((y1A>(y1B+delta)) && (y2A>(y1B+delta)) && (y1A>(y2B+delta)) && (y2A>(y2B+delta))) || ((y1A<(y1B-delta)) && (y2A<(y1B-delta)) && (y1A<(y2B-delta)) && (y2A<(y2B-delta)))) return result; result=overlapped(x1A,y1A,x2A,y2A,x1B,y1B,x2B,y2B,delta); return result; }; int TSimpleMolecule::hasOverlapped(double delta, bool findFirst) { int i, j; bool test; double r,xx,yy; int result=0; for (i=1; i<(nBonds()-1); i++) for (j=i+1; jat[0] == getBond(j)->at[0]) || (getBond(i)->at[0] == getBond(j)->at[1]) || (getBond(i)->at[1] == getBond(j)->at[0]) || (getBond(i)->at[1] == getBond(j)->at[1]); if (! test) if (bondsOverlapped(i,j,delta)) { result++; if (findFirst) return result; }; }; for (i=0; i<(nAtoms()-1); i++) for (j=i+1; jrx-getAtom(j)->rx; yy=getAtom(i)->ry-getAtom(j)->ry; r=sqrt(xx*xx+yy*yy); if (r < (2*delta)) { result++; if (findFirst) return result; }; }; return result; }; bool TSimpleMolecule::checkOverlapped() { bool result; result=hasOverlapped(averageBondLength()/32,true)>0; return result; }; double TSimpleMolecule::averageBondLength() { double result=0; if (nBonds() == 0) return result; for (int i=0; ilist1(listarSize()); double r, xc, yc, xo, yo, xn, yn; int i; int nB1; if (cHB < 0) return; test=makeFragment(nB1,list1,getBond(cHB)->at[1],getBond(cHB)->at[0]); if (nB1 > 1) { //One of the atoms haven't neighbours-flip unavalable cHA1=getBond(cHB)->at[0]; cHA2=getBond(cHB)->at[1]; xc=getAtom(cHA2)->rx-getAtom(cHA1)->rx; //Axes direction calculation yc=getAtom(cHA2)->ry-getAtom(cHA1)->ry; r=sqrt(xc*xc+yc*yc); xc=xc/r; yc=yc/r; xo=xc*xc-yc*yc; yo=2*xc*yc; for (i=0; irx-getAtom(cHA1)->rx; yc=getAtom(n)->ry-getAtom(cHA1)->ry; xn=xc*xo+yc*yo; yn=xc*yo-yc*xo; getAtom(n)->rx=getAtom(cHA1)->rx+xn; getAtom(n)->ry=getAtom(cHA1)->ry+yn; }; }; }; void TSimpleMolecule::bondEnlarge(int bN) { int nB; double r; std::vector list(listarSize()); int cHA1, cH1, cH2, n; bool test; double xc, yc, xc1, yc1; int i; for (i=0; iat[0]; test=makeFragment(nB,list,cHA1,getBond(bN)->at[1]); if (list[0] == getBond(bN)->at[0]) //center definition { cH1=getBond(bN)->at[0]; cH2=getBond(bN)->at[1]; } else { cH1=getBond(bN)->at[1]; cH2=getBond(bN)->at[2]; }; xc=getAtom(cH1)->rx-getAtom(cH2)->rx; yc=getAtom(cH1)->ry-getAtom(cH2)->ry; r=sqrt(xc*xc+yc*yc); xc=xc/r; yc=yc/r; xc1=getAtom(cH2)->rx-getAtom(cH1)->rx; yc1=getAtom(cH2)->ry-getAtom(cH1)->ry; r=r*2; for (i=0; irx=getAtom(n)->rx+xc1+r*xc; getAtom(n)->ry=getAtom(n)->ry+yc1+r*yc; }; }; bool compareAtoms(int a1, int a2, const std::vector *> aeqList) { std::vector * l1; std::vector * l2; int i; bool result=false; if ((a1 < 0) || (a2 < 0) || (a1 >= aeqList.size()) || (a2 >= aeqList.size())) return result; l1 = (std::vector *)(aeqList.at(a1)); l2 = (std::vector *)(aeqList.at(a2)); if ((l1 == NULL) || (l2 == NULL)) return result; if (l1->size() != l2->size()) return result; result=true; for (i=0; isize(); i++) if ((*l1)[i] != (*l2)[i]) { result=false; break; }; return result; }; int TSimpleMolecule::fragmentSecond(int sphere, int att, int secAt, const std::vector a, const std::vector b, const neigbourlist bk, std::vector& wSphere) { /*Generate a longinteger number, which is characterize fragment with atom CHA in center. Up to second neigbour's sphere are took into account. Array A contains the atom's codes, generated by ALLATATOM, array B-bond's codes, generated by BONDCONVERSION*/ int i,j,k,n,i1,i2,i3,jj; std::vector aDist(listarSize()); std::vector aList(listarSize()); std::vector atomoBond(listarSize()); std::vector * aPrev [3]; bool test,testOK; int w2; int l2,l3,tl1,tl2; std::vector ab(listarSize()); int result=0; // String s; if (nAtoms()==0) return result; for (j=0; j<3; j++) aPrev[j]=new std::vector(listarSize()); for (i=0; i 0) for (j=0; jat[0]; if (i2 == i1) i2=getBond(i3)->at[1]; if ((aDist[i2] >= k) && (aDist[i2] <= 65700)) { //if the neighbour has not been added yet to list} aDist[i2]=k; //Mark neighbour's sphere number jj=0; test=true; for (jj=1; jj<=3; jj++) { if (((*aPrev[jj-1])[i2] == -1) || (jj == 3)) { (*aPrev[jj-1])[i2]=i1; break; }; }; w2=a[i2]; w2=w2 << 6; w2=w2+b[i3]; w2=w2 ^ atomoBond[i2]; //atomoBond[I2]:=AtomoBond[I2] xor W2; !!!!!!! atomoBond[i2]=w2; }; }; }; if (secAt < 0) testOK=(k==sphere); else testOK=((k>=sphere) && (aDist[secAt] < 65700)); if (testOK) test=false; }; //end WHILE // At this point I have sphere numbers in Adist, corresponding AtomoBond word, number of atom(s), from //which the desirable atom were generated for (i=0; i=1; i1--) { n=0; for (i=0; i 0) { i2=0; for (i=0; i= 0) { tl1=ab[aList[i]]; tl2=atomoBond[(*aPrev[j-1])[aList[i]]]; // [Aprev[J][AList[I]]]; tl2=tl2 << 9; tl1=tl1 ^ tl2; ab[aList[i]]=tl1; }; }; //Sorting if (n > 1) for (i=0; i<(n-1); i++) for (j=i+1; j 1) i2=15/(n-1); l2=ab[aList[0]]; if (n > 1) for (i=1; inc; if (l3 != 0) l2=-l2; for (i=0; i& equivalenceList, bool isTopologyOnly) { //AllAboutCycles must be called early std::vector a(listarSize()); std::vector b(listarSize()); TSimpleMolecule * em; int i,j,n; neigbourlist bk; std::vector *> aeqList(0); std::vector * lL; em= new TSimpleMolecule(); em->moleculeCopy(*this); //Only topology-nothing more!!!! if (isTopologyOnly) { for (i=0; inAtoms(); i++) { em->getAtom(i)->na=6; em->getAtom(i)->nv=4; em->getAtom(i)->rl=6; em->getAtom(i)->nc=0; }; for (i=0; inBonds(); i++) em->getBond(i)->tb=1; em->defineAtomConn(); } else { for (i=0; inBonds(); i++) if ((em->getBond(i)->tb >= 9) && (em->getBond(i)->tb <= 11)) em->getBond(i)->tb=1; }; em->defineBondConn(bk); // for (i=0; inAtoms(); i++) a[i]=em->getAtom(i)->allAtAtom(); for (i=0; inBonds(); i++) b[i]=em->getBond(i)->bondConversion(); for (i=0; inAtoms(); i++) { lL=new vector(em->listarSize()); for (j=0; jsize(); j++) (*lL)[i]=0; em->fragmentSecond(em->nAtoms(),i,-1,a,b,bk,*lL); aeqList.push_back(lL); }; equivalenceList.resize(em->nAtoms()); n=0; for (i=0; inAtoms(); i++) if (equivalenceList[i] == 0) { n++; equivalenceList[i]=n; if (i < (em->nAtoms()-1)) for (j=i+1; jnAtoms(); j++) if (equivalenceList[j] == 0) { if (compareAtoms(j,i,aeqList)) equivalenceList[j]=n; } }; //freeing resorces for (int i=0; i& currentValues, const std::vector maxValues) { int i,l; bool result=false; for (i=0; i currentValues(nBonds()); std::vector maxValues(nBonds()); std::vector rotBondList(nBonds()); int at1,at2; int nn; r=averageBondLength()/blDenominator; k=hasOverlapped(r,false); result=k; smCopy->moleculeCopy(*this); test=true; while (test) { test=false; for (i=0; idb == 0) { flipSmall(i); kk=hasOverlapped(r,false); if (kkmoleculeCopy(*this); } else moleculeCopy(*smCopy); }; if (k == 0) test=false; }; result=k; if (result > 0) //multiply bonds rotation... { smCopy->moleculeCopy(*this); for (i=0; idb == 0) { at1=getBond(i)->at[0]; at2=getBond(i)->at[1]; if ((getAtom(at1)->nb >= 2) && (getAtom(at2)->nb >=2)) { //checking if greter 1 test=false; if (getAtom(at1)->nb == 3) { test=true; for (j=0; jnb; j++) { k=getAtom(at1)->ac[j]; if ((k != at2) && (getAtom(k)->nb > 1)) test=false; }; }; if ((! test) && (getAtom(at2)->nb == 3)) { test=true; for (j=0; jnb; j++) { k=getAtom(at2)->ac[j]; if ((k != at1) && (getAtom(k)->nb > 1)) test=false; }; }; if (! test) rotBondList[i]=1; }; }; nn=0; for (i=0; i0)) //All possible combinations of rotations... { currentValues.resize(nn); maxValues.resize(nn); for (i=0; imoleculeCopy(*smCopy); test=true; while (test && (k>0)) { moleculeCopy(*smCopy); n=0; for (i=0; imoleculeCopy(*this); }; test=incrementValues(currentValues,maxValues); };// (k=0) or (not test); moleculeCopy(*bestStore); result=k; }; }; if (result>0) //Bond enlarge....] { k=hasOverlapped(r,false); n=1; while (n != 0) { n=0; for (i=0; idb == 0) { smCopy->moleculeCopy(*this); smCopy->bondEnlarge(i); kk=smCopy->hasOverlapped(r,false); if (kk < k) { k=kk; n=i; }; smCopy->flipSmall(i); kk=smCopy->hasOverlapped(r,false); if (kk < k) { k=kk; n=-i; }; }; if (abs(n) != 0) { bondEnlarge(abs(n)); if (n < 0) flipSmall(abs(n)); }; }; result=k; }; //freeing resources if (smCopy != NULL) delete(smCopy); if (bestStore != NULL) delete(bestStore); return result; }; //******************************************************************************** // Determine Bond orders from attached hydrogens //******************************************************************************* void deleteIntElement(std::vector * source, int index) { int i,n; std::vector temp(source->size()-1); n=0; for (i=0; isize(); i++) if (i != index) { temp[n]=(*source)[i]; n++; }; source->resize(source->size()-1); for (i=0; isize(); i++) (*source)[i]=temp[i]; }; int hydrogenValency(int na) //Hydrogen valency { int result=0; if (na < NELEMMCDL) result=hVal[na]; return result; }; int maxValency(int na) //Maximal valency of a specified element { int result=8; if (na < NELEMMCDL) result=maxVal[na]; return result; }; static int findAlternateSinglets(const std::vectoriA1, const std::vectoriA2, const std::vector nH, const std::vector hydrogenValency, std::vector& bondOrder, int nAtoms, int nBonds) { //return values: 0 - no singlet found, 1 - found and assigned singlet, 2 - assigned singlet only with special std::vectornUnassigned(nAtoms); std::vectornAssigned(nAtoms); std::vectorbNumber(nAtoms); int i,n,k; int result; result=0; for (i=0; i 0) && (nUnassigned[i] == 1)) { n=bNumber[i]; k=hydrogenValency[i]-(nH[i]+nAssigned[i]); if (k < 1) { if (k == 0) //increment H valence by 2 { k=2; } else { k=1; }; result=2; }; if (k > 3) { k=3; result=3; }; bondOrder[n]=k; if (result == 0) { result=1; }; }; return result; }; static void makeAssignment(const std::vector iA1, const std::vector iA2, const std::vector nH, const std::vector hydrogenValency, const std::vector bondAssignment, const std::vector specialFlag, std::vector& bondOrder, int nAtoms, int nBonds, int & nAss) { int i,k; nAss=0; for (i=0; i iA1,const std::vector iA2, const std::vector nH, const std::vector hydrogenValency, const std::vector maxValency, const std::vector bondOrder, const std::vector atomCheckFlag, int nAtoms, int nBonds, int & nGtMax, int & nNEH, int & nOddEven, bool testExceedHydrogen, bool oddEvenCheck) { std::vectornBondsValency(nAtoms); //dynamically allocation int i,k; bool result; nGtMax=0; nNEH=0; nOddEven=0; for (i=0; i maxValency[i]) nGtMax++; if (testExceedHydrogen) { if ((nH[i]+nBondsValency[i]) != hydrogenValency[i]) nNEH++; } else { if ((hydrogenValency[i] > 0) && (nH[i] > 0)) if ((nH[i]+nBondsValency[i]) != hydrogenValency[i]) nNEH++; if ((hydrogenValency[i] > 0) && (nH[i] ==0)) if (nBondsValency[i] < hydrogenValency[i]) nNEH++; if (oddEvenCheck) { k=nH[i]+nBondsValency[i]; if ((k % 2) != (maxValency[i] % 2)) nOddEven++; }; }; }; result=((nGtMax == 0) && (nNEH == 0) && (nOddEven == 0)); return result; }; static bool incrementAssignment(std::vector& bondAssignment, int nAss) { int i,j; bool result; result=false; for (i=nAss-1; i>=0; i--) if (bondAssignment[i] == 1) { bondAssignment[i]++; if (i < (nAss-1)) for (j=i+1; j iA1, const std::vector iA2, const std::vector nH, const std::vector maxValency,std::vector& bondOrder, std::vector& hydrogenValency, int nAtoms, int nBonds, bool oddEvenViolate) { //On input BortOrder has to be initialized. Real bond orders have to be putted. 0 means this order should be determined //MaxValency and HydrogenValency and NH are required only for those atoms, which are included in BondOrder=0 std::vectornNeighbour(nAtoms); std::vectorbondAssignment(nBonds); //Should be Max(Atoms, Bonds); std::vectorbondOrderStore(nBonds); std::vectorspecialFlag(nBonds); int i,j,k,n,nAss; bool test; int nGtMax; //number of atoms, for which maximal valency is exausted int nNEH; //bumber of atoms, for which number of calc and desirable hydrogens are disagree int nOddEven;//Parity violation - atom with odd maximal valency has even valency and back int result; k=nAtoms; if (nBonds > k) k=nBonds; k++; result=0; for (i=0; i 0) { n=nH[i]+nNeighbour[i]; if (n > hydrogenValency[i]) hydrogenValency[i]=hydrogenValency[i]+2; if (n > hydrogenValency[i]) hydrogenValency[i]=hydrogenValency[i]+2; }; //Label all bonds which have order 1 exactly for (i=0; i 0) && (nH[i] > 0)) { n=nH[i]+nNeighbour[i]; if (n == hydrogenValency[i]) for (j=0; j 0) { n=nH[i]+nNeighbour[i]; if (n > hydrogenValency[i]) hydrogenValency[i]=hydrogenValency[i]+2; if (n > hydrogenValency[i]) hydrogenValency[i]=hydrogenValency[i]+2; }; //below array NNeighbour is used for picking atoms, for which valency have to be checked.. for (i=0; i aPosition,const std::vector aCharge, const std::vector aRad,const std::vector nHydr, const std::vector iA1, const std::vector iA2, std::vector & bondOrders, int nAtoms, int nBonds) { std::vector hVal(nAtoms); std::vector maxVal(nAtoms); int i,result; for (i=0; i 0) { if (aRad[i] != 0) hVal[i]=hVal[i]-1; //returns 0 for non-radical and 2 for radical if (aPosition[i] == 5) hVal[i]=hVal[i]-aCharge[i]; //B else if (aPosition[i] == 6) hVal[i]=hVal[i]-abs(aCharge[i]); //C else hVal[i]=hVal[i]+aCharge[i]; //Heteroatoms if (hVal[i] < 0) hVal[i]=0; }; maxVal[i]=maxValency(aPosition[i]); if (aCharge[i] != 0) maxVal[i]=maxVal[i]+1; }; result=determineBondsOrder(iA1,iA2,nHydr,maxVal,bondOrders,hVal,nAtoms,nBonds,true); return result; }; int alternate(OBMol * pmol, const std::vector nH, std::vector& bondOrders) { //This overloaded method does not work. By unknown reason I cannot extract //connection matrix from pmol //number of hydogens must be filled on input. std::vector hVal(pmol->NumAtoms()); std::vector maxVal(pmol->NumAtoms()); std::vectoriA1(pmol->NumBonds()); std::vectoriA2(pmol->NumBonds()); int nAtoms,nBonds; int i,k,na; OBAtom * sa; OBBond * sb; int result=0; pmol->AssignSpinMultiplicity(); nAtoms=pmol->NumAtoms(); nBonds=pmol->NumBonds(); for (i=0; iGetBond(i); iA1[i]=sb->GetBeginAtomIdx()-1; iA2[i]=sb->GetEndAtomIdx()-1; }; for (i=1; i<=nAtoms; i++) { sa=pmol->GetAtom(i); na=sa->GetAtomicNum(); hVal[i-1]=hydrogenValency(na); if (hVal[i-1] > 0) { if (sa->GetSpinMultiplicity() != 0) hVal[i-1]=hVal[i-1]-1; //returns 0 for non-radical and 2 for radical k=sa->GetFormalCharge(); if (sa->IsHeteroatom()) hVal[i-1]=hVal[i-1]+k; else if (na == 6) hVal[i-1]=hVal[i-1]-abs(k); else hVal[i-1]=hVal[i-1]-k; if (hVal[i-1] < 0) hVal[i-1]=0; }; maxVal[i-1]=maxValency(na); if (sa->GetFormalCharge() != 0) maxVal[i-1]=maxVal[i-1]+1; }; result=determineBondsOrder(iA1,iA2,nH,maxVal,bondOrders,hVal,nAtoms,nBonds,true); for (i=0; iGetBond(i); sb->SetBondOrder(bondOrders[i]); }; return result; }; //***************************************************************************** //******************* EditedMolecule class *********************************** //***************************************************************************** class TEditedMolecule: public TSimpleMolecule { protected: neigbourlist queryBK; neigbourlist structureBK; std::vector queryQHydr; std::vector queryAGer; std::vector queryBQCounter; std::vector queryCurrentAssignment; std::vector queryEnum; std::vector queryInverse; bool queryStereoQ,fIsQueryPrepare; std::vector aSTested; std::vector bSTested; std::vector bSTestedStore; bool aEQ[NATOMSMAX][NATOMSMAX]; bool bEQ[NBONDSMAX][NBONDSMAX]; void removeHydrogen(std::vector * qHydr, std::vector * qEnumerator); void atomBondChange(); bool stereoBondChange(); void directBondAss(int& bnq, bool& test, bool& test1, const bool beq[NBONDSMAX][NBONDSMAX], const bool aeq[NATOMSMAX][NATOMSMAX], std::vector& bqcounter, std::vector& aqtested, std::vector& bstested, std::vector& bqtested, std::vector& astested, const std::vector ager, const neigbourlist bqconn, const neigbourlist bsconn, TSimpleMolecule * smol); bool allQueryPresent(const std::vector qA, const std::vector qB, int nA, int nB); public: static int const NOOTHER_MASK=1; static int const AROMATIC_MASK=NOOTHER_MASK << 1; static int const EXACTNUMBER_MASK=AROMATIC_MASK << 1; bool fIOPT10; //charge sensitivite bool fIOPT11; //isotopes difference int fIOPT12; //stereo bond change bool fIOPT13; //semipolar bond as double std::vector queryAQTested; std::vector queryBQTested; std::vector* fIncludedList; TEditedMolecule() { TSimpleMolecule(); fIncludedList=NULL; }; int prepareQuery(TSimpleMolecule & sMol); bool fragmentSearch(TEditedMolecule * molecule1, std::vector* bondLabel); TEditedMolecule * extractFragment(int atomN, std::vector * enumerator); void addAsTemplate(TEditedMolecule& fragmentMol, int thisAN, int smAN, int thisBN, int smBN, bool isAddition); int addFragment(TEditedMolecule & eMolecule, int naDEF, int cha, int chb, int chb1, std::vector& list, double xOldCenter, double yOldCenter, double xNewCenter, double yNewCenter, double scale, double cFi, double sFi, int buttonStatus, bool clearEnumerator); }; int TEditedMolecule::addFragment(TEditedMolecule & eMolecule, int naDEF, int cha, int chb, int chb1, std::vector& list, double xOldCenter, double yOldCenter, double xNewCenter, double yNewCenter, double scale, double cFi, double sFi, int buttonStatus, bool clearEnumerator) { // The procedure is used by TEMPLATE and MAKEPOLI procedure to add large frag- // ments to structure. The connection between structure and fragment may be // achived both through atoms (CHA>0) and through bonds (CHB>0). // Variables description: // NAtoms-number of atoms in structure // NBonds-number of bonds in structure // ATOM-atom's attributes in structure // BOND-bond's attributes in structure // CONN-some invariants of bond-connection matrix // NAtoms1, NBonds1, ATOM1, BOND1, CONN1-the same for fragments (these varia- // bles can contain data for more, than one fragments) // LIST-atom's number in arrays ATOM1,CONN1, which should be added to structure // NaDef-number of principal components in LIST array // CHA-atom's number in array ATOM (structure), which was selected for addition // CHB-bond's number in array BOND (structure), which was selected for addition // CHB1-bond's number in array BOND1 (fragment), which was selected for addition // XOldCenter,YOldCenter-old coordinates of the fragment on screen (center rotation) // XNewCenter,YNewCenter-new coordinates of the fragment on screen // Scale-scale variables to resize fragment // CFi,SFi-cosine and sine of the angle of rotation of the fragment relative // old coordinate of ths type of a fragment addition (active only for addition // through atoms). If e fragment on screen // ButtonStatus-regulateButtonStatus=1 then fragment will be added to structure // (new bond arise). If ButtonStatus=2 then fragment will be freezed in // structure (selected new atom for structure will be deleted, no new bond // will be arised)} // Returns 0 if addition is OK, otherwise: // =1 - Maxima number of atoms is reached // =2 - Maximal number of bonds is reached int i,j1,j2,na,k1,k2,j,nb,n1,n2; int l1=0; int l2=0; bool test; double r1,r2,r3,r4,emBLength; std::vector bTested(NBONDSMAX); std::vector bList(NBONDSMAX); std::vector aList(NBONDSMAX); TSingleAtom * sa=NULL; TSingleBond * sb=NULL; int result=0; //return result; //Analizing on boundaries} k2=1; k1=1; if ((nAtoms()+naDEF) >= NATOMSMAX) { result=1; return result; } //Analizing of bond's nb=0; emBLength=0; for (i=0; iat[0]; n2=eMolecule.getBond(i)->at[1]; if ((bList[n1] != 0) || (bList[n2] != 0)) { nb++; emBLength=emBLength+eMolecule.bondLength(i); } } if (nb > 0) emBLength=emBLength/nb; if ((nb+nBonds()) >= NBONDSMAX) { result=2; return result; } na=nAtoms(); nb=nBonds(); if (chb >= 0) //Bond was selected for addition { l1=getBond(chb)->at[0]; //L1,L2,K1,K2-atoms numbers for corresponding bonds l2=getBond(chb)->at[1]; //in structure and in fragment k1=eMolecule.getBond(chb1)->at[0]; k2=eMolecule.getBond(chb1)->at[1]; j=0; for (i=0; i 0)) { if (nBonds() == 0) r1=DEFAULTBONDLENGTH; else r1=averageBondLength(); scale=r1/emBLength; } //End addition for (i=0; irx-xOldCenter; r2=eMolecule.getAtom(list[i])->ry-yOldCenter; r3=scale*(r1*cFi-r2*sFi); r4=scale*(r1*sFi+r2*cFi); sa=eMolecule.getAtom(list[i])->clone(); sa->rx=xNewCenter+r3; sa->ry=yNewCenter+r4; if (clearEnumerator) sa->enumerator=0; sa->fragIndex=0; addAtom(sa); } if (chb >= 0) { //specific for bond-to-bond addition block-determination of correspondence //of pair of atoms, which make bond, in fragment to the same pair of atoms in //structure. Determination is perfomed through internuclear distances} r1=getAtom(l2)->rx-getAtom(nAtoms()-1)->rx; r2=getAtom(l2)->ry-getAtom(nAtoms()-1)->ry; r3=r1*r1+r2*r2; r1=getAtom(l1)->rx-getAtom(nAtoms()-1)->rx; r2=getAtom(l1)->ry-getAtom(nAtoms()-1)->ry; r4=r1*r1+r2*r2; if (r3 < r4) { k2=nAtoms()-1; k1=nAtoms()-2; } else { k2=nAtoms()-2; k1=nAtoms()-1; } } for (i=0; i= 0) && (i != chb1))) { //addition of bonds from fragment to structure test=false; j1=-1; do //Search, if bond I from BOND1 is presented in the fragment { j1++; if (eMolecule.getBond(i)->at[0] == list[j1]) test=true; } while (! (test || (j1 == (naDEF-1)))); if (test) //Bond is presented-addition { j2=-1; do //Search for second atom, which makes I-th bond in fragment j2++; while (eMolecule.getBond(i)->at[1] != list[j2]); sb=new TSingleBond(); j1=na+j1; //atoms, which make the bond, renumeration j2=na+j2; sb->tb=eMolecule.getBond(i)->tb; //attributes copy sb->at[0]=j1; sb->at[1]=j2; if (clearEnumerator) sb->enumerator=0; addBond(sb); } } if (chb >= 0) //bond-to-bond addition { for (i=0; iat[0] == k1) getBond(i)->at[0]=l1; if (getBond(i)->at[0] == k2) getBond(i)->at[0]=l2; if (getBond(i)->at[1] == k1) getBond(i)->at[1]=l1; if (getBond(i)->at[1] == k2) getBond(i)->at[1]=l2; } //Two atoms deleted... delete(fAtom[nAtoms()-1]); delete(fAtom[nAtoms()-2]); fAtom.resize(nAtoms()-2); //two atoms, which make the bond to be added in fragment, //are deleted. They occupy last positions in arrays ATOM and CONN due to //initial sorting /* Contains mistake-commented to the better time i=0; do { //Two or three bond connection is calculated j=na; if (j < nAtoms()) do { //Search for concided atoms rr1=getAtom(i)->rx-getAtom(j)->rx; rr2=getAtom(i)->ry-getAtom(j)->ry; lR=sqrt(rr1*rr1+rr2*rr2); test=((i != j) & ((lR) <= 3)); if (test) { //Concided pair of atoms found for (k=0; kat[0] == j) getBond(k)->at[0]=i; if (getBond(k)->at[1] == j) getBond(k)->at[1]=i; } deleteAtom(j); //Deletion of atom k=0; do { //Search for bonds, which have identical pair of atom or which l=k; do { test=(getBond(k)->at[0] == getBond(l)->at[0]) && (getBond(k)->at[1] == getBond(l)->at[1]); test=test || (getBond(k)->at[0] == getBond(l)->at[1]) && (getBond(k)->at[1] == getBond(l)->at[0]); test=test || (getBond(l)->at[0] == getBond(l)->at[1]); if (test) { //Bad bonds was found-deletion deleteBond(l); l--; } l++; } while (l < nBonds()); k++; } while (k < (nBonds()-1)); j--; } j++; } while (j < nAtoms()); i++; } while (i < na); */ } if ((buttonStatus>1) && (chb < 0)) { //if fragment 'is freezed' through atom, the atom needs to be deleted for (i=0; iat[0] == cha) getBond(i)->at[0]=na; if (getBond(i)->at[1] == cha) getBond(i)->at[1]=na; } defineAtomConn(); deleteAtom(cha); //atom deleting }; //new values for attribute array defineAtomConn(); //Search for badly-connected aromatic bonds /* commented to better time. Use alternation bond instead if (chb>=0) { for (i=0; icurrvalence > maxVal[getAtom(i)->na]); i++; } while (! (test || (i == nAtoms()))); if (test) { //Creation aromtic bond list n=0; aList[0]=i-1; doubleSearch=true; do { j=nb; test=false; if (j < nBonds()) do { test=((getBond(j)->at[0] == aList[n]) || (getBond(j)->at[1] == aList[n])); if (doubleSearch) test=(test && (getBond(j)->tb == 2)); else test=(test && (getBond(j)->tb == 1)); test=(test && (bTested[j] == 0)); j++; } while (! (test || (j == nBonds()))); if (test) { //Addition to the aromatic list bList[n]=j-1; bTested[j]=1; l=getBond(j-1)->at[0]; if (l == aList[n]) l=getBond(j-1)->at[1]; n++; aList[n]=l; doubleSearch= ! doubleSearch; } } while (test); if (n > 1) { //Alternation of aromatic chain for (j=1; j list(NBONDSMAX); std::vector listA(NBONDSMAX); std::vector listB(NBONDSMAX); double r,scale,xOld,yOld,xNew,yNew,xCenter,yCenter; double r1; double r2; double xu1; double yu1; double xu2; double yu2; double cAngle; double rxTemp1,ryTemp1; int mouseButton=0; int n; double amin; if (fragmentMol.nAtoms() == 0) return; naDef1=smAN; test1=true; test2=false; xCenter=0; yCenter=0; if (thisBN >= 0) { naDef1=fragmentMol.getBond(smBN)->at[0]; test1=false; test2=true; } scale=1; xOld=0; yOld=0; test=fragmentMol.makeFragment(naNum,list,naDef1,-1); //creation of template's fragment //Scale definitions if (thisAN >= 0) //connection through atoms { xCenter=this->getAtom(thisAN)->rx; yCenter=this->getAtom(thisAN)->ry; naDef=thisAN; scale=1; r1=0; r2=0; if (fragmentMol.getAtom(naDef1)->nb > 0) { amin=100000000; n=1; for (i=0; inb; i++) { rxTemp1=fragmentMol.getAtom(naDef1)->rx-fragmentMol.getAtom(fragmentMol.getAtom(naDef1)->ac[i])->rx; ryTemp1=fragmentMol.getAtom(naDef1)->ry-fragmentMol.getAtom(fragmentMol.getAtom(naDef1)->ac[i])->ry; r1=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); if(r1 < amin) { amin=r1; n=i; }; }; rxTemp1=fragmentMol.getAtom(naDef1)->rx-fragmentMol.getAtom(fragmentMol.getAtom(naDef1)->ac[n])->rx; ryTemp1=fragmentMol.getAtom(naDef1)->ry-fragmentMol.getAtom(fragmentMol.getAtom(naDef1)->ac[n])->ry; r1=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); } else r1=DEFAULTBONDLENGTH; if (this->getAtom(naDef)->nb > 0) { amin=100000000; n=1; for (i=0; igetAtom(naDef)->nb; i++) { rxTemp1=this->getAtom(naDef)->rx-this->getAtom(this->getAtom(naDef)->ac[i])->rx; ryTemp1=this->getAtom(naDef)->ry-this->getAtom(this->getAtom(naDef)->ac[i])->ry; r2=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); if(r2 < amin) { amin=r2; n=i; }; }; rxTemp1=this->getAtom(naDef)->rx-this->getAtom(this->getAtom(naDef)->ac[n])->rx; ryTemp1=this->getAtom(naDef)->ry-this->getAtom(this->getAtom(naDef)->ac[n])->ry; r2=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); } else { if (this->nBonds() > 0) r2=this->averageBondLength(); else r2=DEFAULTBONDLENGTH; } if (r1 > 0) scale=r2/r1; else scale=1; } else if (thisBN >= 0) //connection through bonds { rxTemp1=fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[0])->rx-fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[1])->rx; ryTemp1=fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[0])->ry-fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[1])->ry; r1=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); rxTemp1=this->getAtom(this->getBond(thisBN)->at[0])->rx-this->getAtom(this->getBond(thisBN)->at[1])->rx; ryTemp1=this->getAtom(this->getBond(thisBN)->at[0])->ry-this->getAtom(this->getBond(thisBN)->at[1])->ry; r2=sqrt(rxTemp1*rxTemp1+ryTemp1*ryTemp1); if (r1 > 0) scale=r2/r1; else scale=1; } //Old coordinates definition if (smAN >= 0) { xOld=fragmentMol.getAtom(smAN)->rx; yOld=fragmentMol.getAtom(smAN)->ry; } if (thisBN >= 0) { xOld=(fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[0])->rx+fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[1])->rx)/2; yOld=(fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[0])->ry+fragmentMol.getAtom(fragmentMol.getBond(smBN)->at[1])->ry)/2; } //New coordinate definition if (thisBN >= 0) //connection through bonds { xNew=(this->getAtom(this->getBond(thisBN)->at[0])->rx+this->getAtom(this->getBond(thisBN)->at[1])->rx)/2; yNew=(this->getAtom(this->getBond(thisBN)->at[0])->ry+this->getAtom(this->getBond(thisBN)->at[1])->ry)/2; } else //connection through atoms} { if (isAddition) //addition of fragment { this->unitVector(thisAN,xu1,yu1); if (xu1 > -0.999) r=yu1/(1+xu1); else if (yu1 > 0) r=1E9; else r=-1E9; cAngle=2*180*atan(r)/PI; if (true) { cAngle=(int)(cAngle/30); cAngle=30*cAngle; r=cAngle*PI/180; xu1=cos(r); yu1=sin(r); } xNew=r1*scale*xu1+this->getAtom(thisAN)->rx; yNew=r1*scale*yu1+this->getAtom(thisAN)->ry; } else //freezing of fragment { if (this->getAtom(thisAN)->nb == 0) { xu1=1; yu1=0; } else if (this->getAtom(thisAN)->nb == 1) { xu1=this->getAtom(thisAN)->rx-this->getAtom(this->getAtom(thisAN)->ac[0])->rx; yu1=this->getAtom(thisAN)->ry-this->getAtom(this->getAtom(thisAN)->ac[0])->ry; //yu1=this.fAtom.getRY(thisAN)-this.fAtom.getRY(this.fAtom.getAC(thisAN,1)); r2=sqrt(xu1*xu1+yu1*yu1); if (r2 > 1.0E-4) { xu1=xu1/r2; yu1=yu1/r2; } else { xu1=1; yu1=0; } } else this->unitVector(thisAN,xu1,yu1); if (xu1 > -0.999) r=yu1/(1+xu1); else if (yu1 > 0) r=1E9; else r=-1E9; cAngle=2*180*atan(r)/PI; if (true) { cAngle=(int)(cAngle/30); cAngle=30*cAngle; r=cAngle*PI/180; xu1=cos(r); yu1=sin(r); } xNew=this->getAtom(thisAN)->rx; yNew=this->getAtom(thisAN)->ry; } } //Angle definition if (thisAN >= 0) //connection through atoms { fragmentMol.unitVector(smAN,xu2,yu2); xu2=-xu2; yu2=-yu2; r1=xu1*xu2+yu1*yu2; r2=yu1*xu2-xu1*yu2; nBondsOld=this->nBonds(); nAtomsOld=this->nAtoms(); if (isAddition) mouseButton=1; else mouseButton=2; this->addFragment(fragmentMol,naNum,thisAN,thisBN,smBN,list, xOld,yOld,xNew,yNew,scale,r1,r2,mouseButton,false); if (isAddition) { this->addBond(1,nAtomsOld,thisAN); } else nAtomsOld=nAtomsOld-1; } else if ((smBN >= 0) && (thisBN >= 0)) //connection through bonds { this->bondUnitVector(thisBN,xu1,yu1); fragmentMol.bondUnitVector(smBN,xu2,yu2); xu2=-xu2; yu2=-yu2; r1=xu1*xu2+yu1*yu2; r2=yu1*xu2-xu1*yu2; nBondsOld=this->nBonds(); nAtomsOld=this->nAtoms(); addFragment(fragmentMol,naNum,thisAN,thisBN,smBN,list, xOld,yOld,xNew,yNew,scale,r1,r2,1,false); } } TEditedMolecule * TEditedMolecule::extractFragment(int atomN, std::vector * enumerator) { std::vector list(listarSize()); std::vector inverseList(listarSize()); int i,j,k; int na; bool test; TSingleAtom * sa; TSingleBond * sb; TEditedMolecule * result=NULL; if ((atomN < 0) || (atomN >= nAtoms())) return result; if (enumerator != NULL) for (i=0; i1) for (i=0; i<(na-1); i++) for (j=i+1; jlist[j]) { k=list[i]; list[i]=list[j]; list[j]=k; }; if (na > 0) for (i=0; i 0) for (i=0; igetAtom(list[i])->clone(); result->addAtom(sa); if (enumerator != NULL) (*enumerator)[list[i]]=i; } if (nBonds() > 0) for (i=0; iat[0]] >= 0) { sb=getBond(i)->clone(); sb->at[0]=inverseList[getBond(i)->at[0]]; sb->at[1]=inverseList[getBond(i)->at[1]]; result->addBond(sb); } return result; } int TEditedMolecule::prepareQuery(TSimpleMolecule & sMol) { /*Returns error code: 0 - no error -1 - input molecule is not assigned or number of atoms equal zero -2 - unconnected fragments were detected -3 - an exception take place*/ TEditedMolecule * molecule1; int i,j,k,l; bool test,test1,test2,test3; int aQ1,aQ2,bNQ; int result; bool whileCondition,whileCondition1; result=-1; if (sMol.nAtoms()==0) return result; sMol.defineAtomConn(); sMol.allAboutCycles(); //Three above line instead of this line: CalculateAllIndeces(SMOl); moleculeCopy(sMol); result=0; fIsQueryPrepare=true; for (i=0; ina==104) && (! fIOPT11)) getAtom(i)->na=1; if (! fIOPT11) getAtom(i)->iz=0; }; //Initializinf arrays queryQHydr.resize(listarSize()); queryAGer.resize(listarSize()); queryAQTested.resize(listarSize()); queryBQTested.resize(listarSize()); queryBQCounter.resize(listarSize()); queryCurrentAssignment.resize(listarSize()); aSTested.resize(listarSize()); bSTested.resize(listarSize()); bSTestedStore.resize(listarSize()); //initializing for (i=0; ispecial; l=1; if (nAtoms()>2) { j=queryBK[0].nb; for (i=1; ij; test2=(getAtom(l)->na<104) && (getAtom(l)->na!=6); test3=(getAtom(i)->na<104) && (getAtom(i)->na!=6); if ((test1 && (! test2)) || (test3 && (! test2)) || (test1 && test2 && test3)) { j=k; l=i; }; }; }; queryAQTested[0]=0; //Sequence for query assignment begin from the atom aQ1=0; bNQ=-1; test=true; if (nBonds()>0) while (test) //Define sequence to match query and structure { i=0; whileCondition=true; while (whileCondition) { test=(queryAQTested[i] != -1); if (test) { j=0; whileCondition1=true; if (queryBK[i].nb>0) while (whileCondition1) { k=queryBK[i].adjusted[j]; j++; test=(bSTested[k]==-1); whileCondition1=test || (j>=queryBK[i].nb); whileCondition1=! whileCondition1; }; if (queryBK[i].nb==0) test=false; }; i++; whileCondition=test || (i==nAtoms()); whileCondition=! whileCondition; }; if (test) //connected atom found { i--; //zero-index aQ2=i; whileCondition=true; while (whileCondition) { j=0; //search for query bond appended to AQ2, which has not been assigned yet whileCondition1=true; k=0; test1=true; if (queryBK[aQ2].nb>0) while (whileCondition1) { k=queryBK[aQ2].adjusted[j]; test1=(bSTested[k]==-1); j++; whileCondition1=test1 || (j>=queryBK[aQ2].nb); whileCondition1=! whileCondition1; } else { k=0; test1=false; }; if (test1) //found { bNQ++; bSTested[k]=bNQ; //store, that BNQ has been assigned l=getBond(k)->at[1]; //atoms enumeration if (l==aQ2) { l=getBond(k)->at[0]; getBond(k)->at[1]=l; getBond(k)->at[0]=aQ2; }; test1=(queryAQTested[l]==-1); //Make AGER array for end-of cycle BNQ bond} if (test1) { queryAGer[bNQ]=l; aQ1=aQ1+1; queryAQTested[l]=aQ1; aQ2=l; } else aQ2=-1; }; whileCondition=(! test1) || (aQ2==-1); whileCondition=! whileCondition; }; }; }; //Detection of unconnected fragment} for (i=0; i=0) { //Creation of inverse enumeration queryInverse.resize(queryAQTested.size()); for (j=0; jmoleculeCopy(*this); for (i=0; igetAtom(queryAQTested[i])->atomCopy(this->getAtom(i)); for (i=0; i0) for (i=0; i0) for (i=0; igetBond(bSTested[i])->bondCopy(this->getBond(i)); bSTestedStore.resize(bSTested.size()); for (i=0; i0) for (i=0; igetBond(i)->at[0]=queryAQTested[molecule1->getBond(i)->at[0]]; molecule1->getBond(i)->at[1]=queryAQTested[molecule1->getBond(i)->at[1]]; }; //Enumeration of this->moleculeCopy(*molecule1); defineAtomConn(); //Store NO OTHER atom attribute for (i=0; ispecial=aSTested[i]; defineBondConn(queryBK); j=0; //Search for last non-special atom in query} queryStereoQ=stereoBondChange(); //Stereo bond conversion }; delete(molecule1); return result; }; void TEditedMolecule::removeHydrogen(std::vector * qHydr, std::vector * qEnumerator) { //Explicitly-defined hydrogens are removed from the structure, which is characte- //rized of total number of atoms NA, total number of bonds NB, atom's attribute //array ATOM, bond's attribute array BOND, bond-connection matrix invariants //ONN. Number of explicitly-defined hydrogens for each atom are stored in //QHYDR. If QUERYLABEL points on some atom, the value is recalculated so that //the same atom is pointed after hydrogens have been removed from the structure //NB! QueryLabel should be array!} int i,j,i1; bool test,test1,test2; if (qEnumerator != NULL) { if (qEnumerator->size() != nAtoms()) qEnumerator->resize(nAtoms()); for (i=0; isize(); i++) (*qEnumerator)[i]=i; }; for (i=0; ina==104)) getAtom(i)->na=1; //D->H if no isotop sensitivity} }; i=0; test1=false; if (nAtoms()>0) while (ina==1) //hydrogen found { test1=true; j=0; i1=0; test2=true; if (nBonds()>0) while (test2) //search for corresponding bond and hydrogen's neighbour detection { test=(getBond(j)->at[0]==i) || (getBond(j)->at[1]==i); if (test) { i1=getBond(j)->at[0]; if (i1==i) i1=getBond(j)->at[1]; }; test2=test || (j==nBonds()); test2=! test1; j++; }; deleteAtom(i); if ((qHydr != NULL) && (i<(nAtoms()-1))) deleteIntElement(qHydr,i); if (i1>i) i1=i1-1; //shift of attribute's arrays if (qEnumerator != NULL) { for (j=0; jsize(); j++) { if ((*qEnumerator)[j] == i) (*qEnumerator)[j]=-i1; else if ((*qEnumerator)[j] > i) (*qEnumerator)[j]=(*qEnumerator)[j]-1; else if (((*qEnumerator)[j] < 0) && (abs((*qEnumerator)[j]) > i)) (*qEnumerator)[j]=(*qEnumerator)[j]+1; }; }; if ((i1 > 0) && (qHydr != NULL)) (*qHydr)[i1]=(*qHydr)[i1]+1; //counter of explicitly defined hydrogens i--; }; i++; }; if (test1) { defineAtomConn(); //Inverse enumerator creation... New QA->OldQA array} if (qEnumerator != NULL) { //??? Is it required ?} }; }; }; void TEditedMolecule::atomBondChange() { //substitutes the semipolar bond with double. NBONDS-total number of bonds, //BOND-bond's attributes, ATOM-atom's attributes} int i; int ca,cb; if (nBonds()==0) return; for (i=0; iat[0])->nc; cb=getAtom(getBond(i)->at[1])->nc; if ((((ca<0) && (cb>0)) || ((ca>0) && (cb<0))) && ((getBond(i)->tb<3) || (getBond(i)->tb>8))) { if (ca<0) { getAtom(getBond(i)->at[0])->nc=ca+1; getAtom(getBond(i)->at[1])->nc=cb-1; }; if (ca>0) { getAtom(getBond(i)->at[0])->nc=ca-1; getAtom(getBond(i)->at[1])->nc=cb+1; }; if (getBond(i)->tb<3) getBond(i)->tb=getBond(i)->tb+1; else getBond(i)->tb=2; }; }; }; bool TEditedMolecule::stereoBondChange() { //Subtitute stereo bonds with single. On output TEST has TRUE value, if at //least one stereo bond was detected in structure with bond attributes BOND //nd total number of bonds NBONDS int i; bool result; result=false; if ((nBonds()==0) || (fIOPT12==2)) return result; //If Exact Stereo Search-no substitution for (i=0; itb==11) getBond(i)->tb=1; if (getBond(i)->tb>=9) result=true; }; if (fIOPT12==1) if (getBond(i)->tb>=9) getBond(i)->tb=1; //If No Stereo search-all bonds substitution }; return result; }; void TEditedMolecule::directBondAss(int& bnq, bool& test, bool& test1, const bool beq[NBONDSMAX][NBONDSMAX], const bool aeq[NATOMSMAX][NATOMSMAX], std::vector& bqcounter, std::vector& aqtested, std::vector& bstested, std::vector& bqtested, std::vector& astested, const std::vector ager, const neigbourlist bqconn, const neigbourlist bsconn, TSimpleMolecule * smol) { //Assign a structure atom to query atom. Variables: //BNQ-query bond's number, which needs to be assigned //BEQ,AEQ-boolean matrix, contains list of equivalent bonds //BQCONN,BSCONN-for each atom containes list of connected bonds in query and // structure respectively. //QBOND,SBOND-bond's attributes for query and structure respectively //ASTESTED- I-th element of the array contains the query atom's number, which // has been assigned to I-th atom of structure (=0-no assignment) //AQTESTED- I-th element of the array contains the structure atom's number, // which has been assigned to I-th atom of query (=0-no assignment) //BSTESTED- I-th element of the array contains the query bond's number, which // has been assigned to I-th bond of structure (=0-no assignment) //BQTESTED- I-th element of the array contains the structure bond's number, // which has been assigned to I-th bond of query (=0-no assignment) //BQCOUNTER-for each query bond contains bond's number in array BSCONN(!)-con- // nected to corresponding structure atom bonds. The structure bonds // in array BSCONN with less than or equal number have been tested on // assignment, with above number-no. //AGER-for each query bond contains query atom's number, which must be generated // at assignment of this bond to structure or zero, if no atom may be gene- // rated. This array is used for cyclic conditions definition (last bond in // the cycle must be created between already-defined query atoms-no genera- // tion). //TEST-on output contains TRUE, if assignment was successfull, FALSE otherwise //TEST1-on output contains TRUE if all bonds, connected to last assigned atom // in structure will be tested, FALSE otherwise. If TEST1=TRUE and TEST= // FALSE it means, that last atom in structure had been unproperly assigned // -backstep is required to reassign the atom.*/ int bns,bs,as1,as2,aq2,aq1; bool whiletest; test=false; aq1=getBond(bnq)->at[0]; //first query atom, already assigned as1=aqtested[aq1]; //corresponding first structure atom if (as1<0) return; aq2=getBond(bnq)->at[1]; //second query atom} bs=bqcounter[bnq]; bns=0; as2=0; whiletest=true; if (bsgetBond(bns)->at[0]; //second structure atom} if (as2==as1) as2=smol->getBond(bns)->at[1]; test=false; //if no new structure atom assignment must be generated-checking, if AS2 // equal already defined atom for query atom AQ2} if ((ager[bnq]<0) && (astested[as2]>=0)) test=(aqtested[aq2]==as2); //if new atom in structure must be assigned-testing equivalence with //corresponding query if ((ager[bnq]>=0) && (astested[as2]<0)) test=aeq[as2][aq2]; }; bs=bs+1; whiletest=(bs==bsconn[as1].nb) || test; whiletest=! whiletest; }; //until(BS=BSCONN[AS1].NB) or TEST; //until all list of structure bonds will be exausted or success in assignment if (test) //Success { if (ager[bnq]>=0) //if new atom has been assigned-store assignment} { aqtested[aq2]=as2; astested[as2]=aq2; }; bstested[bns]=bnq; //store assignment of bonds} bqtested[bnq]=bns; }; bqcounter[bnq]=bs; //last used structure bond's number in SCONN array} test1=(bs==bsconn[as1].nb); //test, if SCONN for atom given is exausted} }; bool TEditedMolecule::allQueryPresent(const std::vector qA, const std::vector qB, int nA, int nB) { //the function returns TRUE if for all query atoms and bonds the equivalent //structure atom (bond) can be found, FALSE otherwise. QA-for each query atom //contains '1' if a structure atom may be associated with the query, '0' other- //wise. The same is true for bond's list QB. NA-total number of query atoms, //NB-total number of query bonds int i; bool test,whiletest; i=0; if (nA<0) return false; whiletest=true; test=false; while (whiletest) { test=qA[i]==1; i++; whiletest=(! test) || (i==nA); whiletest=! whiletest; }; if (test && (nB>=0)) { i=0; whiletest=false; while (whiletest) { test=qB[i]==1; i++; whiletest=(! test) || (i==nB); whiletest=! whiletest; }; }; return test; }; bool TEditedMolecule::fragmentSearch(TEditedMolecule * molecule1, std::vector* bondLabel) { int cycleNumber; int j,k,l,m,mm; bool test; bool test1; bool test2,test3,stereoS; int aq1,aq2,as1,as2,j1,i1; int ii; bool result=false; bool whiletest1,whiletest2; if ((molecule1==NULL) || (! fIsQueryPrepare)) return result; if (molecule1->nAtoms()==0) return result; if (molecule1->listarSize()>aSTested.size()) aSTested.resize(molecule1->listarSize()); if (molecule1->listarSize()>bSTested.size()) bSTested.resize(molecule1->listarSize()); if (molecule1->listarSize()>queryQHydr.size()) queryQHydr.resize(molecule1->listarSize()); if (molecule1->listarSize()>queryAQTested.size()) queryAQTested.resize(molecule1->listarSize()); if (molecule1->listarSize()>queryBQTested.size()) queryBQTested.resize(molecule1->listarSize()); if (molecule1->listarSize()>queryCurrentAssignment.size()) queryCurrentAssignment.resize(molecule1->listarSize()); if (this->listarSize()>queryAQTested.size()) queryAQTested.resize(this->listarSize()); for (int i=0; inAtoms(); i++) for (j=0; jnBonds(); i++) for (j=0; j0) { j=0; if (molecule1.fBruttoFormula.nD>0) { whiletest1=true; while (whiletest1) { j++; k=0; test1.value=(fBruttoFormula.eList[j]==104) && (! fIOPT11); whiletest2=true; if ((fBruttoFormula.eList[j] != 1) && (! test1.value)) while (whiletest2) { k++; test2=((fBruttoFormula.eList[j]==molecule1.fBruttoFormula.eList[k]) && (fBruttoFormula.eNumber[j]<=molecule1.fBruttoFormula.eNumber[k])); whiletest2=test2 || (k==molecule1.fBruttoFormula.nD); whiletest2=! whiletest2; } else test2=true; whiletest1=(! test2) || (j==fBruttoFormula.nD); whiletest1=! whiletest1; }; } else test2=false; } else test2=true; */ test2=true; //turn of molecular formula filter molecule1->fIOPT11=fIOPT11; molecule1->fIOPT12=fIOPT13; molecule1->fIOPT13=fIOPT13; if (test2) { //{R/S/Z/E description are removed: they are not used in substructure search} if (fIOPT13) molecule1->atomBondChange(); //Semipolar bond conversion molecule1->defineAtomConn(); molecule1->defineBondConn(structureBK); stereoS=molecule1->stereoBondChange(); //Stereo bond conversion} for (j=0; jnAtoms(); j++) { //Isotops conversion} if ((molecule1->getAtom(j)->na==104) && (! fIOPT11)) molecule1->getAtom(j)->na=1; if (! fIOPT11) molecule1->getAtom(j)->iz=0; bSTested[j]=0; //bsTested contains nu. explicit hydrogens - so 0 is fine! }; for (j=0; jnAtoms(); j++) if (molecule1->getAtom(j)->na==1) if (molecule1->getAtom(j)->nb>0) for (k=0; kgetAtom(j)->nb; k++) { //Formation of list explicitly-defined hydrogens} l=molecule1->getAtom(j)->ac[k]; bSTested[l]=bSTested[l]+1; }; for (k=0; knAtoms()>0) while (whiletest1) { test2=TSingleAtom::atomEquivalent(molecule1->getAtom(j),getAtom(0),bSTested[j],queryQHydr[0],fIOPT10,fIOPT11); //Addition for aromatic search} if (test2 && ((getAtom(0)->special & AROMATIC_MASK)!=0) ) { test2=false; for (m=0; m<=structureBK[j].nb; m++) { mm=structureBK[j].adjusted[m]; if ((molecule1->getBond(mm)->db==2) || (molecule1->getBond(mm)->db==3)) { test2=true; break; }; }; }; //End addition} if (test2) { if (queryEnum.size()<1) queryEnum.resize(1); queryEnum[0]=0; if (queryAQTested.size()<1) queryAQTested.resize(1); queryAQTested[0]=j; }; j++; whiletest1=test2 || (j==molecule1->nAtoms()); whiletest1=! whiletest1; }; } else if (nBonds()==0) test2=false; else { //General case - substructure search if (fIncludedList != NULL) { for (j=0; jnAtoms(); j++) for (k=0; knAtoms(); j++) for (k=0; kgetAtom(j),getAtom(k),bSTested[j], queryQHydr[k],fIOPT10,fIOPT11) && (getAtom(k)->nb<=molecule1->getAtom(j)->nb)); //Addition for aromatic search if (test3 && ((getAtom(k)->special & AROMATIC_MASK) !=0)) { test3=false; for (m=0; mgetBond(mm)->db==2) || (molecule1->getBond(mm)->db==3)) { test3=true; break; }; }; }; aEQ[j][k]=test3; if (test3) aSTested[k]=1; }; }; for (j=0; jnBonds(); j++) for (k=0; kat[0]; aq2=getBond(k)->at[1]; as1=molecule1->getBond(j)->at[0]; as2=molecule1->getBond(j)->at[1]; test2=(aEQ[as1][aq1] && aEQ[as2][aq2]) || (aEQ[as1][aq2] && aEQ[as2][aq1]); if (test2) { test3=TSingleBond::bondEquivalent(molecule1->getBond(j),getBond(k)); bEQ[j][k]=test3; if (test3) queryAQTested[k]=1; } else bEQ[j][k]=false; }; //Check, if all query atoms has a partner in structure test2=allQueryPresent(aSTested,queryAQTested,nAtoms(),nBonds()); if (test2) { j1=0; for (k=1; knAtoms(); k++) if (aEQ[k][0]) { //Collection of structure atoms, which may be associated with first query atom j=0; whiletest1=true; while (whiletest1) { test=bEQ[structureBK[k].adjusted[j]][0]; if (test) { queryCurrentAssignment[j1]=k; j1++; }; j++; whiletest1=test || (j==structureBK[k].nb); whiletest1=! whiletest1; }; }; i1=0; whiletest1=true; if (j1>0) while (whiletest1) //for each structure, which may be assigned to 1-st query { for (j=0; jnBonds(); j++) bSTested[j]=-1; for (j=0; jnAtoms(); j++) aSTested[j]=-1; queryAQTested[0]=queryCurrentAssignment[i1]; aSTested[queryCurrentAssignment[i1]]=0; ii=0; whiletest2=true; while (whiletest2) //start recursion { directBondAss(ii,test,test1,bEQ,aEQ,queryBQCounter,queryAQTested,bSTested, queryBQTested,aSTested,queryAGer,queryBK,structureBK,molecule1); if ((! test) && (ii>=1)) { //previous atom were badly assigned-backstep queryBQCounter[ii]=0; k=queryAGer[ii-1]; if (k>=0) { l=queryAQTested[k]; queryAQTested[k]=-1; aSTested[l]=-1; }; bSTested[queryBQTested[ii-1]]=-1; queryBQTested[ii-1]=-1; ii=ii-2; test=true; }; ii=ii+1; //query bond counter whiletest2=(ii==nBonds()) || ((! test) && (ii==1)); whiletest2=! whiletest2; }; i1++; test2=(ii==nBonds()); //Checking, if success has been reached whiletest1=((j1==i1) || test2); whiletest1=! whiletest1; } else test2=false; }; }; }; if ((bondLabel != NULL) && (nBonds()>0)) { bondLabel->resize(molecule1->nBonds()); if (test2) for (j=0; jnBonds(); j++) if (bSTested[j]>=0) (*bondLabel)[j]=1; else (*bondLabel)[j]=0; }; result=test2; return result; }; //*************************************************************************** // Diagram generation routines //*************************************************************************** class PartFragmentDefinition { public: int fragID1,fragID2,fragID3,fragFirstAtomNo; int fragmentCount; double fragWidth,fragHeight,fragTop,fragLeft; PartFragmentDefinition() { }; void assign(const PartFragmentDefinition source) { fragID1=source.fragID1; fragID2=source.fragID2; fragID3=source.fragID3; fragFirstAtomNo=source.fragFirstAtomNo; fragmentCount=source.fragmentCount; fragWidth=source.fragWidth; fragHeight=source.fragHeight; fragTop=source.fragTop; fragLeft=source.fragLeft; }; }; class TemplateRedraw { public: TemplateRedraw(); // virtual ~TemplateRedraw() { // clear(); // }; bool isOverlapped(const std::vector list, int fragNo, double xSuggested, double ySuggested); void arrangeFragments(std::vector& list, int fragNo, double aspOptimal); void redrawFine(TSimpleMolecule& smIn); int coordinatesPrepare(TEditedMolecule& sm, int kk, int anTemplateNo); private: static std::vector queryData; void clear(); bool internalBondsPresent(TEditedMolecule * mQuery, TSimpleMolecule * mStructure); void rotateBondVertically(TSimpleMolecule * sm, const std::vectorbondList, int bondNo, double xuValue, double yuValue, double& c1, double& s1, double& xSize, double& ySize, double& xCenter, double& yCenter, int& nVert); void selectFragmentConfiguration(TSimpleMolecule * sm, std::vector* atomList); void rescaleSingleFragment(TSimpleMolecule * sm, std::vector* atomList, int alCount, PartFragmentDefinition& pf, double offset); void arrangeMolecules(std::vector& extendedList, double aspOptimal); bool loadTemplates(); }; //Initialization of static member variable std::vector TemplateRedraw::queryData; bool TemplateRedraw::loadTemplates() { std::ifstream ifs; string filename("templates.sdf");//("e:\\templates.sdf"); TEditedMolecule sm; TEditedMolecule * em; int i,na1,na2; bool test; bool result=false; try { OpenDatafile(ifs, filename); } catch (exception ex) { return result; }; //Read into a vector of OBMol if (ifs) { OBConversion conv(&ifs); conv.SetInFormat("sdf"); OBMol mol; // mol.SetIsPatternStructure(); // modified by Igor - not in OB 2.2.0 while (conv.Read(&mol)) { sm.readOBMol(&mol); mol.Clear(); for (i=0; ina != 1) sm.getAtom(i)->na=ANY_ATOM; sm.getAtom(i)->nc=0; } for (i=0; iat[0]; na2=sm.getBond(i)->at[1]; test=((sm.getAtom(na1)->na != 1) && (sm.getAtom(na2)->na != 1)); if (test) sm.getBond(i)->tb=ANY_BOND; //ANY bons } sm.defineAtomConn(); sm.allAboutCycles(); em=new TEditedMolecule(); em->prepareQuery(sm); queryData.push_back(em); } result=true; obErrorLog.ThrowError(__FUNCTION__, "Read OK " + filename, obInfo); } // else {obErrorLog.ThrowError(__FUNCTION__, "Cannot find " + filename, obWarning);} return result; }; TemplateRedraw::TemplateRedraw() { if(queryData.empty()) //Load internal and external templates only once { int i,j; TEditedMolecule * em; TEditedMolecule * em1; TEditedMolecule * em2; TEditedMolecule sm; bool test; //Load external.. loadTemplates(); //...and internal templates for (i=0; iprepareQuery(sm); queryData.push_back(em); } if (queryData.size()>1) for (i=0; i<(queryData.size()-1); i++) for (j=i+1; jnAtoms() < em2->nAtoms()) test=true; else if (em1->nAtoms() > em2->nAtoms()) test=false; else if (em1->nBonds() < em2->nBonds()) test=true; if (test) { queryData[i]=em2; queryData[j]=em1; } } } } void TemplateRedraw::clear() { for (int i=0; i aList(mStructure->nAtoms()); std::vector bList(mStructure->nBonds()); int i,n1,n2; bool result=false; for (i=0; inAtoms(); i++) aList[mQuery->queryAQTested[i]]=1; for (i=0; inBonds(); i++) bList[mQuery->queryBQTested[i]]=1; for (i=0; igetBond(i)->at[0]; n2=mStructure->getBond(i)->at[1]; if ((aList[n1] == 1) && (aList[n2] == 1)) result=true; if (result) break; }; //seek for 3-attached atom return result; }; void TemplateRedraw::rotateBondVertically(TSimpleMolecule * sm, const std::vectorbondList, int bondNo, double xuValue, double yuValue, double& c1, double& s1, double& xSize, double& ySize, double& xCenter, double& yCenter, int& nVert) { //Dummy rotation of molecule //Two solution - when c2,s2 are used - are determined for cases of rotaition when Up and Down are counterchnged. Single solution would be enough std::vector atomList(NATOMSMAX); int i,n,n1,n2; bool test; double rX,rY,r; std::vector coorX(NATOMSMAX); std::vector coorY(NATOMSMAX); double xMin,xMax,yMin,yMax; xSize=0; ySize=0; c1=1; s1=0; xCenter=0; yCenter=0; nVert=0; for (i=0; inAtoms(); i++) atomList[i]=false; test=false; for (i=0; igetBond(n)->at[0]; n2=sm->getBond(n)->at[1]; atomList[n1]=true; atomList[n2]=true; }; if (! test) return; //bond is absent in the list.... n1=sm->getBond(bondNo)->at[0]; n2=sm->getBond(bondNo)->at[1]; xCenter=sm->getAtom(n2)->rx; yCenter=sm->getAtom(n2)->ry; rX=sm->getAtom(n1)->rx-xCenter; rY=sm->getAtom(n1)->ry-yCenter; r=sqrt(rX*rX+rY*rY); if (r != 0) { rX=rX/r; rY=rY/r; if (abs(rX) < 0.00001) { s1=xuValue/rY; c1=-yuValue/rY; } else if (abs(rY) < 0.00001) { s1=yuValue/rX; c1=xuValue/rX; } else { s1=(xuValue/rX+yuValue/rY)/(rY/rX+rX/rY); c1=(xuValue/rY-yuValue/rX)/(rY/rX+rX/rY); }; }; //c1, s1 analizing for (i=0; inAtoms(); i++) { coorX[i]=c1*(sm->getAtom(i)->rx-xCenter)+s1*(sm->getAtom(i)->ry-yCenter); coorY[i]=s1*(sm->getAtom(i)->rx-xCenter)-c1*(sm->getAtom(i)->ry-yCenter); }; xMin=RUNDEF; xMax=RUNDEF; yMin=RUNDEF; yMax=RUNDEF; for (i=0; inAtoms(); i++) if (atomList[i]) { if ((coorX[i] < xMin) || (xMin == RUNDEF)) xMin=coorX[i]; if ((coorX[i] > xMax) || (xMax == RUNDEF)) xMax=coorX[i]; if ((coorY[i] < yMin) || (yMin == RUNDEF)) yMin=coorY[i]; if ((coorY[i] > yMax) || (yMax == RUNDEF)) yMax=coorY[i]; }; xSize=xMax-xMin; ySize=yMax-yMin; //calculation no. vertical bonds... for (i=0; igetBond(n)->at[0]; n2=sm->getBond(n)->at[1]; //no. vert. bonds r=coorY[n1]-coorY[n2]; if (abs(r) > 0) { r=abs((coorX[n1]-coorX[n2])/r); if (r < 0.02) nVert++; }; }; }; void TemplateRedraw::selectFragmentConfiguration(TSimpleMolecule * sm, std::vector* atomList) { //for each fragment in AtomList select optimal angle of rotation double xMax; double xSize; double ySize; double xCenter; double yCenter; double c; double s; int nVert; double cMax,sMax,x,y; std::vector bondList(0); std::vector bondListAll(0); std::vector atomCleaned(NATOMSMAX); int i,j,n,n1,n2,w; bool test; if (atomList == NULL) { for (i=0; inAtoms(); i++) atomCleaned[i]=true; } else { for (i=0; inAtoms(); i++) atomCleaned[i]=false; for (i=0; isize(); i++) { n=(*atomList)[i]; atomCleaned[n]=true; }; }; //bondList generation... for (i=0; inBonds(); i++) { n1=sm->getBond(i)->at[0]; n2=sm->getBond(i)->at[1]; if (atomCleaned[n1] && atomCleaned[n2]) { w=i; bondListAll.push_back(w); if (sm->getBond(i)->db > 1) bondList.push_back(w); //only ring bonds... }; }; if (bondList.size() == 0) { bondList.resize(bondListAll.size()); for (i=0; i 0) { //search for 4-coordinater non-carbon test=false; n=-1; for (i=0; inAtoms(); i++) if (atomCleaned[i] && (sm->getAtom(i)->na != 6) && (sm->getAtom(i)->nb == 4)) { test=true; for (j=0; jnBonds(); j++) if ((sm->getBond(j)->at[0] == i) || (sm->getBond(j)->at[1] == i)) { n=j; if (sm->getBond(j)->db > 1) { n=-1; test=false; break; }; }; if (test) break; }; if (! test) for (i=0; inBonds(); i++) if (atomCleaned[sm->getBond(i)->at[0]] && (sm->getBond(i)->db == 5)) { n=i; test=true; break; }; if (test) { rotateBondVertically(sm,bondListAll,n,1.0,0.0,c,s,xSize,ySize,xCenter,yCenter,nVert); if (ySize > xSize) rotateBondVertically(sm,bondListAll,n,0.0,1.0,c,s,xSize,ySize,xCenter,yCenter,nVert); cMax=c; sMax=s; } else { xMax=0; cMax=1; sMax=0; for (i=0; i xMax) { xMax=xSize; cMax=c; sMax=s; }; }; for (i=0; i xMax) { xMax=xSize; cMax=c; sMax=s; }; }; }; //Rotation for (i=0; inAtoms(); i++) if (atomCleaned[i]) { x=cMax*(sm->getAtom(i)->rx-xCenter)+sMax*(sm->getAtom(i)->ry-yCenter); y=sMax*(sm->getAtom(i)->rx-xCenter)-cMax*(sm->getAtom(i)->ry-yCenter); sm->getAtom(i)->rx=x; sm->getAtom(i)->ry=y; }; }; }; void TemplateRedraw::rescaleSingleFragment(TSimpleMolecule * sm, std::vector* atomList, int alCount, PartFragmentDefinition& pf, double offset) { int i,n; double xMin,xMax,yMin,yMax; double scale,r1,r2; double x,y; xMin=RUNDEF; xMax=RUNDEF; yMin=RUNDEF; yMax=RUNDEF; for (i=0; igetAtom(n)->rx < xMin) || (xMin == RUNDEF)) xMin=sm->getAtom(n)->rx; if ((sm->getAtom(n)->rx > xMax) || (xMax == RUNDEF)) xMax=sm->getAtom(n)->rx; if ((sm->getAtom(n)->ry < yMin) || (yMin == RUNDEF)) yMin=sm->getAtom(n)->ry; if ((sm->getAtom(n)->ry > yMax) || (yMax == RUNDEF)) yMax=sm->getAtom(n)->ry; }; if ((xMax == xMin) && (yMax == yMin)) { for (i=0; igetAtom(n)->rx=(pf.fragLeft+pf.fragWidth/2); sm->getAtom(n)->ry=(pf.fragTop+pf.fragHeight/2); }; return; }; if (xMin == xMax) //y should be scaled { scale=(pf.fragHeight-2*offset)/(yMax-yMin); } else if (yMin == yMax) //x should be scaled { scale=(pf.fragWidth-2*offset)/(xMax-xMin); } else { r1=(pf.fragHeight-2*offset)/(yMax-yMin); r2=(pf.fragWidth-2*offset)/(xMax-xMin); if (r1 > r2) scale=r2; else scale=r1; }; for (i=0; igetAtom(n)->rx-xMin)*scale; y=pf.fragTop+offset+(sm->getAtom(n)->ry-yMin)*scale; sm->getAtom(n)->rx=x; sm->getAtom(n)->ry=y; }; }; bool ptInRect(const Rect r, const Point p) { bool result=((p.x >= r.left) & (p.x <= r.right) & (p.y >= r.top) & (p.y <= r.bottom)); return result; } bool TemplateRedraw::isOverlapped(const std::vector list, int fragNo, double xSuggested, double ySuggested) { // !!! Zero-based fragno !!! //checking if recteangles are overlapped from different fagments. Rect rect; Point p; int i,j,n1,n2; PartFragmentDefinition * ef; PartFragmentDefinition * efTemp; bool test, result; result=false; ef=(PartFragmentDefinition *)list[fragNo]; rect.left=xSuggested; rect.top=ySuggested; rect.right=rect.left+ef->fragWidth; rect.bottom=rect.top+ef->fragHeight; for (i=0; ifragLeft+efTemp->fragWidth) < rect.left); if (! test) test=((efTemp->fragTop+efTemp->fragHeight) < rect.top); if (! test) test=(efTemp->fragLeft > rect.right); if (! test) test=(efTemp->fragTop > rect.bottom); if (! test) { n1=(int)(efTemp->fragLeft); n2=(int)(efTemp->fragLeft+efTemp->fragWidth); for (j=n1; j<=n2; j++) { p.x=j; p.y=efTemp->fragTop; if (ptInRect(rect,p)) { result=true; return result; }; p.x=j; p.y=efTemp->fragTop+efTemp->fragHeight; if (ptInRect(rect,p)) { result=true; return result; }; }; n1=(int)(efTemp->fragTop); n2=(int)(efTemp->fragTop+efTemp->fragHeight); for (j=n1; j<=n2; j++) { p.y=j; p.x=efTemp->fragLeft; if (ptInRect(rect,p)) { result=true; return result; }; p.y=j; p.x=efTemp->fragLeft+efTemp->fragWidth; if (ptInRect(rect,p)) { result=true; return result; }; }; }; }; return result; }; void TemplateRedraw::arrangeFragments(std::vector& list, int fragNo, double aspOptimal) { //!! Frag no zero-based! double aspDelta,xNice,yNice,xMax,yMax,xMin,yMin,r,x,y,xNew,yNew; PartFragmentDefinition * ef; PartFragmentDefinition * efInterest; int i; ef=(PartFragmentDefinition *)list[0]; xMax=ef->fragLeft+ef->fragWidth; yMax=ef->fragTop+ef->fragHeight; xMin=ef->fragLeft; yMin=ef->fragTop; //Calculate sizes.... for (i=1; ifragLeft+ef->fragWidth; if (r > xMax) xMax=r; r=ef->fragTop+ef->fragHeight; if (r > yMax) yMax=r; if (ef->fragTop < yMin) yMin=ef->fragTop; if (ef->fragLeft < xMin) xMin=ef->fragLeft; }; aspDelta=1000000; xNice=0; yNice=0; efInterest=(PartFragmentDefinition *)list[fragNo]; for (i=0; ifragLeft+ef->fragWidth+1; y=ef->fragTop; if (! isOverlapped(list,fragNo,x,y)) { if (((x+efInterest->fragWidth) <= xMax) && ((y+efInterest->fragHeight) <= yMax)) { efInterest->fragLeft=x; efInterest->fragTop=y; list[fragNo]=efInterest; return; }; xNew=x+efInterest->fragWidth; if (xMax > xNew) xNew=xMax; yNew=y+efInterest->fragHeight; if (yMax > yNew) yNew=yMax; r=(yNew-yMin)/(xNew-xMin); if (abs(r-aspOptimal) < aspDelta) { xNice=x; yNice=y; aspDelta=abs(r-aspOptimal); }; }; x=ef->fragLeft; y=ef->fragTop+ef->fragHeight+1; if (! isOverlapped(list,fragNo,x,y)) { if (((x+efInterest->fragWidth) <= xMax) && ((y+efInterest->fragHeight) <= yMax)) { efInterest->fragLeft=x; efInterest->fragTop=y; list[fragNo]=efInterest; return; }; xNew=x+efInterest->fragWidth; if (xMax > xNew) xNew=xMax; yNew=y+efInterest->fragHeight; if (yMax > yNew) yNew=yMax; r=(yNew-yMin)/(xNew-xMin); if (abs(r-aspOptimal) < aspDelta) { xNice=x; yNice=y; aspDelta=abs(r-aspOptimal); }; }; }; //if here - maxX,0 and 0,MaxY have to be tested... x=xMax; y=0; xNew=x+efInterest->fragWidth; if (xMax > xNew) xNew=xMax; yNew=y+efInterest->fragHeight; if (yMax > yNew) yNew=yMax; r=(yNew-yMin)/(xNew-xMin); if (abs(r-aspOptimal) < aspDelta) { xNice=x; yNice=y; aspDelta=abs(r-aspOptimal); }; x=0; y=yMax; xNew=x+efInterest->fragWidth; if (xMax > xNew) xNew=xMax; yNew=y+efInterest->fragHeight; if (yMax > yNew) yNew=yMax; r=(yNew-yMin)/(xNew-xMin); if (abs(r-aspOptimal) < aspDelta) { xNice=x; yNice=y; aspDelta=abs(r-aspOptimal); }; efInterest->fragLeft=xNice; efInterest->fragTop=yNice; list[fragNo]=efInterest; }; void TemplateRedraw::arrangeMolecules(std::vector& extendedList, double aspOptimal) { int i,j; PartFragmentDefinition * ef; PartFragmentDefinition * efTemp; double maxX,minY,minX,maxY,r,r1; if (extendedList.size() > 1) { //Search for Max Width and MaxHeight fragments for (i=0; i<(extendedList.size()-1); i++) for (j=i+1; jfragWidth > ef->fragWidth) { extendedList[j]=ef; extendedList[i]=efTemp; }; }; if (extendedList.size() > 2) { ef=(PartFragmentDefinition *)extendedList[1]; for (i=2; ifragHeight > ef->fragHeight) { extendedList[i]=ef; extendedList[1]=efTemp; ef=efTemp; }; }; }; //Try to arrange 1-st and 2-nd fragments.... ef=(PartFragmentDefinition *)extendedList[0]; efTemp=(PartFragmentDefinition *)extendedList[1]; maxX=ef->fragWidth+efTemp->fragWidth; minY=efTemp->fragHeight; if (ef->fragHeight > minY) minY=ef->fragHeight; r=minY/maxX; minX=ef->fragWidth; maxY=ef->fragHeight+efTemp->fragHeight; r1=maxY/minX; if (abs(r-aspOptimal) < abs(r1-aspOptimal)) { if (ef->fragHeight > efTemp->fragHeight) { efTemp->fragLeft=ef->fragWidth; extendedList[1]=efTemp; } else { ef->fragLeft=efTemp->fragWidth; extendedList[0]=ef; }; } else { efTemp->fragTop=ef->fragHeight; extendedList[1]=efTemp; }; //Arranging of 3-rd and more frgments... if (extendedList.size()>2) for (i=2; i listAtomClean(NBONDSMAX); std::vector listBondClean(NBONDSMAX); std::vector templateAtomNumber(0); std::vector fragmentAtomNumber(0); bool test,test1; std::vector groupsFind(0); TEditedMolecule tm; TEditedMolecule * emTemplate; std::vector enumerator(0); TSingleBond * sb; int nFound; TEditedMolecule * smFragment; TEditedMolecule * smRest; int naStore; int result; sm.defineAtomConn(); sm.allAboutCycles(); result=-1; for (i=kk; ifragmentSearch(&sm,NULL)) { em=(TEditedMolecule *)queryData[i]; if (! internalBondsPresent(em,&sm)) { result=i+1; break; }; em=NULL; }; if (em == NULL) { if (anTemplateNo >= 0) { sm.addAtom(6,0,0.0,0.0); sm.addBond(1,anTemplateNo,sm.nAtoms()-1); sm.defineAtomConn(); }; sm.redrawMolecule(); if (anTemplateNo >= 0) { sm.deleteBond(sm.nBonds()); sm.deleteAtom(sm.nAtoms()-1); sm.defineAtomConn(); }; } else { //Hurrah! Found! Redraw from template listAtomClean.resize(NBONDSMAX); listBondClean.resize(NBONDSMAX); templateAtomNumber.resize(0); fragmentAtomNumber.resize(0); //Atom list formation atomClean=0; for (i=0; inAtoms(); j++) { k=em->queryAQTested[j]; if (i==k) { //Coordinates assigning sm.getAtom(i)->rx=em->getAtom(j)->rx; sm.getAtom(i)->ry=em->getAtom(j)->ry; test=true; break; }; }; if (test) //Only those atoms, defined at template, are cleaned..... { listAtomClean[atomClean]=i; atomClean++; }; }; enumerator.resize(sm.nAtoms()); //index-old number value-new number for (i=0; iclone()); }; //Bond enumeration for (i=0; iclone(); sb->at[0]=enumerator[sb->at[0]]; sb->at[1]=enumerator[sb->at[1]]; tm.addBond(sb); }; nFound=atomClean; //now tm is the same as sm (simplemolecule) on input, but coordinates will be taken from template for first nFound atoms tm.defineAtomConn(); tm.allAboutCycles(); //search for connected bonds test=true; while (test) { test=false; for (i=tm.nBonds()-1; i>=0; i--) { test1=((tm.getBond(i)->at[0]<=nFound) && (tm.getBond(i)->at[1]>nFound)) || ((tm.getBond(i)->at[1]<=nFound) && (tm.getBond(i)->at[0]>nFound)); if (test1) if ((tm.getBond(i)->db<=1) && (tm.getAtom(tm.getBond(i)->at[0])->nb > 1) && (tm.getAtom(tm.getBond(i)->at[1])->nb > 1)) //acyclic bond { test=true; emTemplate=new TEditedMolecule(); if (tm.getBond(i)->at[1] >nFound) fragmentAN=tm.getBond(i)->at[1]; else fragmentAN=tm.getBond(i)->at[0]; //Error is absent! Really 2-nd atom is compared in both operators! if (tm.getBond(i)->at[1] >nFound) templateAN=tm.getBond(i)->at[0]; else templateAN=tm.getBond(i)->at[1]; templateAtomNumber.push_back(tm.getAtom(templateAN)->enumerator); //in template.... fragmentAtomNumber.push_back(tm.getAtom(fragmentAN)->enumerator); tm.deleteBond(i); //must be called prior extract fragment tm.defineAtomConn(); smFragment=tm.extractFragment(fragmentAN,NULL); smRest=tm.extractFragment(templateAN,NULL); tm.moleculeCopy(*smRest); //I have to delete unnecessary atoms and bond here... emTemplate->moleculeCopy(*smFragment); groupsFind.push_back(emTemplate); delete(smFragment); delete(smRest); }; if (test) break; }; }; //Atom clean list formation atomClean=tm.nAtoms()-nFound; for (i=0; iat[0]) test=true; if (listAtomClean[j]==tm.getBond(i)->at[1]) test=true; if (test) break; }; if (test) { listBondClean[bondClean]=i; bondClean++; }; }; //and cleaning.... tm.defineAtomConn(); tm.redraw(listAtomClean,listBondClean,atomClean,bondClean,4,0,0,false); for (i=0; inAtoms(); j++) if (emTemplate->getAtom(j)->enumerator==fragmentAtomNumber[i]) { fragmentAN=j; break; }; emTemplate->refofs=sm.refofs; coordinatesPrepare(*emTemplate,0,fragmentAN); //recursive call templateAN=-1; fragmentAN=-1; for (j=0; jenumerator==templateAtomNumber[i]) { templateAN=j; break; }; for (j=0; jnAtoms(); j++) if (emTemplate->getAtom(j)->enumerator==fragmentAtomNumber[i]) { fragmentAN=j; break; }; naStore=tm.nAtoms(); tm.refofs=sm.refofs; tm.addAsTemplate(*emTemplate,templateAN,fragmentAN,-1,-1,true); if ((naStore+emTemplate->nAtoms()) == tm.nAtoms()) for (j=0; jnAtoms(); j++) { tm.getAtom(naStore+j)->enumerator=emTemplate->getAtom(j)->enumerator; }; }; sm.moleculeCopy(tm); sm.defineAtomConn(); sm.allAboutCycles(); }; for (i=0; i atomTested(smIn.nAtoms()); std::vector atomList(NATOMSMAX); std::vector stereoBondList(0); int w; PartFragmentDefinition * pf; std::vector frList(0); double xMin,xMax,yMin,yMax; double oldBondLength,newBondLength; std::vector molList(0); //contains list of molecules... TEditedMolecule * sm; TEditedMolecule smCopy; TEditedMolecule smCopy2; int frCount,nA; TSingleAtom * sA; TSingleBond * sB; StereoBondStore * sbs; int nOverlapped, nOverlappedMin; //smIn will be clear-nothing oldBondLength=smIn.averageBondLength(); if (oldBondLength < 0.1) oldBondLength=1.0; smCopy.moleculeCopy(smIn); for (i=0; itb == 9) || (smIn.getBond(i)->tb == 10)) { w=0; //!!!! add for STEREO later !!!! w.value=ProcessStereo.analizeRS(smIn,smIn.fBond.getAT(i,1)); if (w>0) { sbs=new StereoBondStore(); sbs->bn=i; sbs->w=w; stereoBondList.push_back(sbs); }; }; for (i=0; ianum=intToStr(i); for (i=0; idefineAtomConn(); molList.push_back(sm); }; int qq=0; for (frCount=0; frCountnAtoms(); i++) sm->getAtom(i)->enumerator=i; n=0; nOverlappedMin=10000000; smCopy2.moleculeCopy(*sm); while (n >=0) { qq++; sm->refofs=smIn.refofs; n=coordinatesPrepare(*sm,n,-1); //Contrary to Delphi project - block of storing double-bonds is absent here. //I need to create AnalizeZE rpocedure to handle double-bond nOverlapped=sm->correctOverlapped(); if (nOverlapped < nOverlappedMin) { smCopy2.moleculeCopy(*sm); nOverlappedMin=nOverlapped; } else sm->moleculeCopy(smCopy2); if (nOverlappedMin == 0) n=-1; }; sm->defineAtomConn(); atomList.resize(sm->nAtoms()); for (i=0; iaverageBondLength(); if (newBondLength > 0) for (i=0; inAtoms(); i++) { sm->getAtom(i)->rx=sm->getAtom(i)->rx*oldBondLength/newBondLength; sm->getAtom(i)->ry=sm->getAtom(i)->ry*oldBondLength/newBondLength; }; //stereo input try }; frList.resize(0); smCopy.clear(); nA=0; for (frCount=0; frCountnAtoms(); i++) { sA=sm->getAtom(i)->clone(); smCopy.addAtom(sA); }; for (i=0; inBonds(); i++) { sB=sm->getBond(i)->clone(); sB->at[0]=sm->getBond(i)->at[0]+nA; sB->at[1]=sm->getBond(i)->at[1]+nA; smCopy.addBond(sB); }; //dimensions store.... xMin=RUNDEF; xMax=RUNDEF; yMin=RUNDEF; yMax=RUNDEF; for (j=0; jnAtoms(); j++) { if ((sm->getAtom(j)->rx < xMin) || (xMin == RUNDEF)) xMin=sm->getAtom(j)->rx; if ((sm->getAtom(j)->rx > xMax) || (xMax == RUNDEF)) xMax=sm->getAtom(j)->rx; if ((sm->getAtom(j)->ry < yMin) || (yMin == RUNDEF)) yMin=sm->getAtom(j)->ry; if ((sm->getAtom(j)->ry > yMax) || (yMax == RUNDEF)) yMax=sm->getAtom(j)->ry; }; pf=new PartFragmentDefinition(); pf->fragID1=0; pf->fragID2=0; pf->fragID3=0; pf->fragFirstAtomNo=nA+1; pf->fragmentCount=1; pf->fragWidth=xMax-xMin; pf->fragHeight=yMax-yMin; pf->fragTop=0; pf->fragLeft=0; frList.push_back(pf); //Prepare for new get nA=nA+sm->nAtoms(); }; smCopy.defineAtomConn(); //arrange fragments atomList.resize(NATOMSMAX); if (frList.size()>1) { newBondLength=smCopy.averageBondLength(); for (i=0; ifragWidth=pf->fragWidth+newBondLength; pf->fragHeight=pf->fragHeight+newBondLength; }; arrangeMolecules(frList,1/1.5); //rescaling in fragments.... for (i=0; ifragFirstAtomNo,-1); rescaleSingleFragment(&smCopy,&atomList,w,*pf,newBondLength/2); }; }; //rescaling to satisfy bond length newBondLength=smCopy.averageBondLength(); if (newBondLength>0) for (i=0; irx=smCopy.getAtom(i)->rx*oldBondLength/newBondLength; smCopy.getAtom(i)->ry=smCopy.getAtom(i)->ry*oldBondLength/newBondLength; }; for (i=0; ianum.c_str()); if (n >= 0) { smIn.getAtom(n)->rx=smCopy.getAtom(i)->rx; smIn.getAtom(n)->ry=smCopy.getAtom(i)->ry; }; }; for (i=0; ibn,1)); !!!! add for STEREO later !!!! if ((w > 0) && (w != sbs->w)) { if (smIn.getBond(sbs->bn)->tb == 9) smIn.getBond(sbs->bn)->tb=10; else smIn.getBond(sbs->bn)->tb=9; }; }; for (i=0; i